/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************************************************************
 * @file     Mram_Ip.c                                                                       *
 * @brief    SSDK mram Driver                                                                      *
 * @author   Department/Semidrive                                                                  *
 * @date     2024/10/16                                                                            *
 *                                                                                                 *
 **************************************************************************************************/

#include "lld_platform.h"
#ifdef CFG_PLATFORM_MCAL
#include "RegHelper.h"
#else
#include "reg.h"
#endif
#include "Mram_Ip.h"
#include "Mram_IpAsm.h"
#include "irq.h"

/* PRQA S 0306,0310,0314,0316,0326,0488,0750,0759,1505,1535,1881,3205,3206,3116,3673,4404 EOF */

/***************************************************************************************************
 *                                 Private Macro definition                                        *
 **************************************************************************************************/
/**
 * mramc software config
 */

#define MRAM_RDBUFFER_NUM       (5u)        /* Maximum number of channels in the readbuffer */
#define MRAM_APB2TMC_DATA_NUM   (5u)        /* The maximum amount of data supported for apb2tmc */
#define MRAM_IMG_CRC_REGION_NUM (8u)        /* Maximum number of image regions */
#define MRAM_SUB_BANK_MAX_NUM   (8u)        /* The maximum number of sub-banks */

// The following configurations are from the "TRM:21.5.8 Secial timing safty"
#define MRAM_MIN_TRDL_NS (200u) /* Min_Trdl=200ns */
#define MRAM_MIN_TRDS_NS (100u) /* Min_Trds=100ns */
#define MRAM_MIN_TWK_NS (4000u) /* Min_Twk=4000ns */
#define MRAM_32M_MIN_TCYCRD_NS (18u) /* Min_Tcycrd=18ns(Mram32Mb) */
#define MRAM_4M_MIN_TCYCRD_NS  (13u) /* Min_Tcycrd=13ns(Mram4Mb) */
#define MRAM_MIN_TPGM_NS (225u) /* Min_Tpgm=225ns */
#define MRAM_MAX_TPGM_NS (275u) /* Max_Tpgm=275ns */
#define MRAM_MID_TPGM_NS ((MRAM_MIN_TPGM_NS + MRAM_MAX_TPGM_NS) / 2u)
#define MRAM_32M_MIN_TW2R_NS (1700u) /* Min_Tw2r=1700u(Mram32Mb) */
#define MRAM_4M_MIN_TW2R_NS (1000u) /* Min_Tw2r=1000u(Mram4Mb) */
#define MRAM_MIN_TR2W_NS (3000u) /* Min_Tr2w=3000ns */
#define MRAM_MIN_TPGM_OTP_NS (100u) /* Min_TpgmOtpRg=100ns */
#define MRAM_MAX_TPGM_OTP_RP_RAP_NS (140u) /* Max_TpgmOtpRpRap=140ns */
#define MRAM_MIN_TPGM_OTP_RBD_NS (9000u) /* Min_TpgmOtpRbd=9000ns */
#define MRAM_MAX_TPGM_OTP_RBD_NS (11000u) /* Max_TpgmOtpRbd=11000ns */

// Polling a register once is counted as 25ns (r52: 16 cpu tick, r5:18 cpu tick; MAX 600M)
#define MRAM_MIN_POLLING_REG_MS_2_TICK(ms)        ((ms) * 40000u)
#define MRAM_MIN_POLLING_REG_US_2_TICK(us)        ((us) * 40u)
#define MRAM_MAX_POLLING_DEFAULT_TIMEOUT          MRAM_MIN_POLLING_REG_MS_2_TICK(5u)  /* 5ms */
#define MRAM_MAX_POLLING_MRAM_READY_TIMEOUT       MRAM_MAX_POLLING_DEFAULT_TIMEOUT  /* 5ms */
#define MRAM_MAX_POLLING_WR_STALL_TIMEOUT         MRAM_MAX_POLLING_DEFAULT_TIMEOUT  /* 5ms */
#define MRAM_MAX_POLLING_APB2TMC_TRIGE_TIMEOUT    MRAM_MAX_POLLING_DEFAULT_TIMEOUT  /* 5ms */
#define MRAM_MAX_POLLING_TMC_BUSY_TIMEOUT         MRAM_MAX_POLLING_DEFAULT_TIMEOUT  /* 5ms */
#define MRAM_MAX_POLLING_RRLUT_LOAD_TRIG_TIMEOUT  MRAM_MAX_POLLING_DEFAULT_TIMEOUT  /* 5ms */
#define MRAM_MAX_POLLING_TMC_CLK_UPD_TIMEOUT      MRAM_MAX_POLLING_DEFAULT_TIMEOUT  /* 5ms */
#define MRAM_MAX_POLLING_SLP_STA_TIMEOUT          MRAM_MAX_POLLING_DEFAULT_TIMEOUT  /* 5ms */
#define MRAM_MAX_POLLING_CRC_TRIG_TIMEOUT(size)   ((size) << 7)  /* (size/16)*T_tmcclk*32*16, min_tmcClk=12M  */
#define MRAM_MAX_POLLING_FLUSH_DONE_TIMEOUT       MRAM_MIN_POLLING_REG_MS_2_TICK(640u) /* (size/128)*16*5ms, maxSize=1K */
#define MRAM_MAX_POLLING_TMB_BIST_READY_TIMEOUT   (0xFFFFFFFFu) /* maximum number of times */
#define MRAM_MAX_POLLING_TMB_TRIGE_TIMEOUT        MRAM_MIN_POLLING_REG_US_2_TICK(10u) /* 10us */
#define MRAM_MAX_POLLING_BIST_DONE_TIMEOUT        (0xFFFFFFFFu) /* maximum number of times */

#define MRAM_32M_MAX_ACLK_HZ (300000000u)   /* The maximum clock frequency of aclk is 300M */
#define MRAM_4M_MAX_ACLK_HZ  (200000000u)   /* The maximum clock frequency of aclk is 200M */
#define MRAM_MAX_TMC_CLK_HZ (50000000u) /* The minimum clock frequency of tmc_clk is 50M */
#define MRAM_MIN_TMC_CLK_HZ (12000000u) /* The minimum clock frequency of tmc_clk is 12M */
#define MRAM_PGLK_PASSWORD (0xAA55AA55AA55AA55ULL)
#define MRAM_PROTECT_ALIGN_SIZE                   (0x20000u) /* Program permission lock area requires 128k alignment */

#define MRAM_IRQ_INT0_DEFAULT_MASK                (0x0001E17Fu)
#define MRAM_IRQ_INT1_DEFAULT_MASK                (0x0000FFFFu)

#define MRAM_TMB_REG_ADDR_MASK                       (0x3Fu) /* TMB Valid address mask */

#define MRAM_NS_2_TICKS(hz, ns) (((hz) / 1000u * (ns)) / 1000000u)  /* Converts nanoseconds to the number of beats based on frequency */
#define MRAM_US_2_TICKS(hz, us) (((hz) / 1000u * (us)) / 1000u)  /* Converts microseconds to the number of beats based on frequency */
#define MRAM_MS_2_TICKS(hz, ms) ((hz) / 1000u * (ms))  /* Convert milliseconds to beats based on frequency */

/**
 * tmc register
 */
/** @brief control ref cell rewrite */
#define MRAM_TMC_R_TEST (0x0u)
#define MRAM_TMC_R_TEST_TEST_ENABLE_LSB (1)

#define MRAM_TMC_R_REWRITE (0x15u)
#define MRAM_TMC_R_REWRITE_OVRD_VAL_MASK (0x3u)
#define MRAM_REL_CELL_REWRITE_DISABLE_CODE (0x55AAu)
#define MRAM_REL_CELL_REWRITE_ENABLE_CODE  (0xAA55u)

/**
 * mramc register
 */
/** @brief mramc global control register0 */
#define MRAM_MRAMC_GLB_CTRL0 (0x0u)
#define MRAM_MRAMC_GLB_CTRL0_TMC_CLK_UPD_BUSY_LSB   (27u)
#define MRAM_MRAMC_GLB_CTRL0_TMC_CLK_UPD_BUSY_MASK  MRAM_BIT(MRAM_MRAMC_GLB_CTRL0_TMC_CLK_UPD_BUSY_LSB)
#define MRAM_MRAMC_GLB_CTRL0_TMC_CLK_DIV_NUM_LSB    (0u)

/** @brief mramc global control register1 */
#define MRAM_MRAMC_GLB_CTRL1 (0x4u)

/** @brief mramc global control register2 */
#define MRAM_MRAMC_GLB_CTRL2 (0x8u)

/** @brief mramc global control register3 */
#define MRAM_MRAMC_GLB_CTRL3 (0xCu)

/** @brief mramc status register */
#define MRAM_MRAMC_STA (0x20u)
#define MRAM_MRAMC_STA_MRAM_READY_MASK MRAM_BIT(0)
#define MRAM_MRAMC_STA_TMC_BUSY MRAM_BIT(1)
#define MRAM_MRAMC_STA_TMC_SLP_STA_MASK MRAM_BIT(2)
#define MRAM_MRAMC_STA_TMC_TMC_INF_BUSY MRAM_BIT(5)

/** @brief mramc function interrupt0 status */
#define MRAM_MRAMC_FUNC_INT0_STA (0x30u)
#define MRAM_MRAMC_FUNC_INT0_STA_WRBUF_FLUSH_DONE_MASK  MRAM_BIT(8)
#define MRAM_MRAMC_FUNC_INT0_STA_WRBUF_LTEQWM_MASK MRAM_BIT(9)
#define MRAM_MRAMC_FUNC_INT0_STA_WRBUF_GTEQWM_MASK MRAM_BIT(10)
#define MRAM_MRAMC_FUNC_INT0_STA_TMC_RECALL_KEY_ERR_LSB    (5u)

/** @brief mramc function interrupt0 status enable */
#define MRAM_MRAMC_FUNC_INT0_STA_EN (0x34u)
#define MRAM_MRAMC_FUNC_INT0_STA_EN_FLUSH_DONE_LSB (8)

/** @brief mramc function interrupt0 signal enable */
#define MRAM_MRAMC_FUNC_INT0_SIG_EN (0x38u)
#define MRAM_MRAMC_FUNC_INT0_SIG_EN_FLUSH_DONE_LSB (8u)
#define MRAM_MRAMC_FUNC_INT0_SIG_EN_MRAM_ERR_ADDR_VLD_LSB (16u)

/** @brief mramc function interrupt1 status */
#define MRAM_MRAMC_FUNC_INT1_STA (0x3Cu)

/** @brief mramc function interrupt1 status enable */
#define MRAM_MRAMC_FUNC_INT1_STA_EN (0x40u)

/** @brief mramc function interrupt1 signal enable */
#define MRAM_MRAMC_FUNC_INT1_SIG_EN (0x44u)

/** @brief AXI2TMC write configure register */
#define MRAM_AXI2TMC_WR_CFG (0x80u)
#define MRAM_AXI2TMC_WR_CFG_WR_STALL_LSB (6u)
#define MRAM_AXI2TMC_WR_CFG_WR_STALL_RDY_MASK MRAM_BIT(7)

/** @brief AXI2TMC write interval configure register */
#define MRAM_AXI2TMC_WR_INTERVAL (0x84u)

/** @brief AXI2TMC write abort configure register */
#define MRAM_AXI2TMC_WR_ABORT (0x88u)

/** @brief AXI2TMC write buffer configure register */
#define MRAM_AXI2TMC_WRBUF_CFG (0xA0u)
#define MRAM_AXI2TMC_WRBUF_CFG_EMPTY_MASK MRAM_BIT(30)
#define MRAM_AXI2TMC_WRBUF_CFG_MAX_ENTRY (64UL)

/** @brief AXI2TMC read configure register */
#define MRAM_AXI2TMC_RD_CFG (0x100u)

/** @brief AXI2TMC read interval configure register */
#define MRAM_AXI2TMC_RD_INTERVAL (0x104u)

/** @brief AXI2TMC read buffer configure0 register */
#define MRAM_AXI2TMC_RDBUF_CFG0 (0x120u)
#define MRAM_AXI2TMC_RDBUF_CFG0_RDBUF_EN_LSB (0u)
#define MRAM_AXI2TMC_RDBUF_CFG0_MASTER_IDENT_LSB (16u)
#define MRAM_AXI2TMC_RDBUF_CFG0_MASTER_ID_MASK  (0x1Fu)
#define MRAM_AXI2TMC_RDBUF_CFG0_AXI_ID_MASK     (0xFFu)

/** @brief AXI2TMC read buffer configure1 register */
#define MRAM_AXI2TMC_RDBUF_CFG1 (0x124u)

/** @brief AXI2TMC read buffer status register */
#define MRAM_AXI2TMC_RDBUF_STA (0x128u)

/** @brief AXI2TMC arbit configure register */
#define MRAM_AXI2TMC_ARBIT_CFG (0x190u)

/** @brief TMC refif data out */
#define MRAM_TMC_REGIF_DOUT (0x2C4u)

/** @brief TMC refif data out */
#define MRAM_ERR_ADDR (0x2C8u)

/** @brief APB2TMC configure register */
#define MRAM_APB2TMC_CFG (0x2A0u)
#define MRAM_APB2TMC_CFG_TRIG_LSB (31)
#define MRAM_APB2TMC_CFG_TRIG_MASK MRAM_BIT(31)
#define MRAM_APB2TMC_CFG_CMD_LSB (20)
#define MRAM_APB2TMC_CFG_ACC_ADDR_LSB (0u)
#define MRAM_APB2TMC_CFG_YADR_MASK (0x1Fu)
#define MRAM_APB2TMC_CFG_XADR_LSB  (5u)
#define MRAM_APB2TMC_CFG_XADR_MASK (0x3FFu)
#define MRAM_APB2TMC_CFG_BADR_LSB  (15u)
#define MRAM_APB2TMC_CFG_BADR_MASK (0x7u)

/** @brief APB2TMC Data register */
#define MRAM_APB2TMC_DATA_31_0      (0x2A4u)
#define MRAM_APB2TMC_DATA_63_32     (0x2A8u)
#define MRAM_APB2TMC_DATA_95_64     (0x2ACu)
#define MRAM_APB2TMC_DATA_127_96    (0x2B0u)
#define MRAM_APB2TMC_DATA_146_128    (0x2B4u)
#define MRAM_APB2TMC_DATA   (MRAM_APB2TMC_DATA_31_0)

/** @brief mram size */
#define MRAM_MRAM_SIZE (0x300u)

/** @brief mram software program lock register */
#define MRAM_MRAM_SWPGLK (0x304u)

#define MRAM_PGLK_PSWDL (0x308u)
#define MRAM_PGLK_PSWDH (0x30Cu)
#define MRAM_PGLK_GPSWDL (0x310u)
#define MRAM_PGLK_GPSWDH (0x314u)

/** @brief  row repair control register */
#define MRAM_ROW_REPCTRL (0x380u)
#define MRAM_ROW_REPCTRL_RRLUT_LOAD_TRIG_MASK MRAM_BIT(1)

#define MRAM_APB2TMB_CTRL (0x400u)
#define MRAM_APB2TMB_CTRL_BIST_EN_LSB (31u)
#define MRAM_APB2TMB_CTRL_BIST_EN_MASK MRAM_BIT(31)
#define MRAM_APB2TMB_CTRL_BIST_BUSY_MASK MRAM_BIT(30)
#define MRAM_APB2TMB_CTRL_TMB_REG_ACC_TRIG_MASK MRAM_BIT(7)

#define MRAM_TMB_REG_DATA_31_0 (0x404u)
#define MRAM_TMB_REG_DATA_39_32 (0x408u)

/** @brief  mram stuck threshold register */
#define MRAM_MRAMC_STUCK_THRESH (0x45cu)

/** @brief  tmc debug ctrl register */
#define MRAM_TMC_EXTRA_CTRL (0x460u)

/** @brief  tmc external rtimer0 register */
#define MRAM_EXT_TMC_RTIMER0 (0x464u)

/** @brief  tmc external rtimer1 register */
#define MRAM_EXT_TMC_RTIMER1 (0x468u)

/** @brief  image crc region start address */
#define MRAM_IMG_CRC_RGN_ADDR (0x1000u)

/** @brief  image crc region size */
#define MRAM_IMG_CRC_RGN_SIZE (0x1004u)

/** @brief  image crc region valid */
#define MRAM_IMG_CRC_RGN_VLD (0x1008u)

/** @brief  image crc region excepted crc32 result value */
#define MRAM_IMG_CRC_RGN_EXPVAL (0x100Cu)

/** @brief  image crc region hardware caculated crc32 value */
#define MRAM_IMG_CRC_RGN_RSLT (0x1010u)

/** @brief  image crc region configure */
#define MRAM_IMG_CRC_RGN_CFG (0x1014u)
#define MRAM_IMG_CRC_RGN_CFG_CRC_SING_TRIG MRAM_BIT(0)

/** @brief  ip revision register */
#define MRAM_REVISION (0xFFF0u)

/***************************************************************************************************
 *                                    Private Type Declarations                                    *
 **************************************************************************************************/


/**
 *  registers data structure
 */
/** @brief mramc global control register0 */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType tmcClkDivNum: 4;
        Mram_BitFieldType tmcRst: 1;
        Mram_BitFieldType tmcClkGate: 1;
        Mram_BitFieldType tmcLpr: 1;
        Mram_BitFieldType tmcSlm: 1;
        Mram_BitFieldType mramRst: 1;
        Mram_BitFieldType mramVddGate: 1;
        Mram_BitFieldType mrmPdmIo: 1;
        Mram_BitFieldType reserved: 16;
        Mram_BitFieldType tmcClkUpdBusy: 1;
        Mram_BitFieldType tmcClkDone: 1;
        Mram_BitFieldType tmcInfStall: 1;
        Mram_BitFieldType mramVddNg: 1;
        Mram_BitFieldType mramVdioNg: 1;
    } data;
} Mram_MramcGlbCtrl0Type;

/** @brief mramc global control register1 */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType tmcCfg2Cpb: 1;
        Mram_BitFieldType tmcSramDma: 1;
        Mram_BitFieldType tmcCfgVblExtVmax: 1;
        Mram_BitFieldType reserved1: 5;
        Mram_BitFieldType rdRetryCnt: 8;
        Mram_BitFieldType reserved2: 16;
    } data;
} Mram_MramcGlbCtrl1Type;

/** @brief mramc global control register2 */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType idle2lprTimeout: 14;
        Mram_BitFieldType idle2lprRatio: 2;
        Mram_BitFieldType idle2slmTimeout: 14;
        Mram_BitFieldType idle2slmRatio: 2;
    } data;
} Mram_MramcGlbCtrl2Type;

/** @brief mramc global control register3 */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType tmcClkHaltCnt: 16;
        Mram_BitFieldType tmcInfMaxHalt: 16;
    } data;
} Mram_MramcGlbCtrl3Type;

/** @brief mramc status register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType mramReady: 1;
        Mram_BitFieldType tmcBusy: 1;
        Mram_BitFieldType tmcSlpSta: 1;
        Mram_BitFieldType tmcLprSta: 1;
        Mram_BitFieldType tmcInfDeny: 1;
        Mram_BitFieldType tmcInfBusy: 1;
        Mram_BitFieldType apb2tmcBusy: 1;
        Mram_BitFieldType axi2tmcBusy: 1;
        Mram_BitFieldType rrldBusy: 1;
        Mram_BitFieldType reserved: 23;
    } data;
} Mram_MramcStaType;

/** @brief mramc function interrupt { status | status enable | signal enable } register0 */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType tmcErr: 1;
        Mram_BitFieldType tmcLoadErr: 1;
        Mram_BitFieldType tmcWriteErr: 1;
        Mram_BitFieldType tmcReadErr: 1;
        Mram_BitFieldType tmcRecallErr: 1;
        Mram_BitFieldType tmcRecallKeyErr: 1;
        Mram_BitFieldType swpglkPswdErr: 1;
        Mram_BitFieldType reserved1: 1;
        Mram_BitFieldType wrbufFlushDone: 1;
        Mram_BitFieldType wrbufLteqwm: 1;
        Mram_BitFieldType wrbufGteqwm: 1;
        Mram_BitFieldType wrbufLtwm: 1;
        Mram_BitFieldType wrbufGtwm: 1;
        Mram_BitFieldType pglockAreaDeny: 1;
        Mram_BitFieldType hsmImgRgnDeny: 1;
        Mram_BitFieldType hsmKeyRgnDeny: 1;
        Mram_BitFieldType mramErrAddVld: 1;
        Mram_BitFieldType reserved2: 14;
        Mram_BitFieldType mramcStuck: 1;
    } data;
} Mram_MramcFuncInt0Type;

/** @brief mramc function interrupt { status | status enable | signal enable } register1 */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType crcSingDone: 8;
        Mram_BitFieldType crcChkFail: 8;
        Mram_BitFieldType reserved: 16;
    } data;
} Mram_MramcFuncInt1Type;

/** @brief AXI2TMC write configure register */

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType wrOtDis: 1;
        Mram_BitFieldType wrRespEn: 1;
        Mram_BitFieldType autoClrLoad: 1;
        Mram_BitFieldType wrbufDis: 1;
        Mram_BitFieldType wrbufEntryMode: 1;
        Mram_BitFieldType wrMergeEn: 1;
        Mram_BitFieldType wrStall: 1;
        Mram_BitFieldType wrStallRdy: 1;
        Mram_BitFieldType earlyRespEn: 1;
        Mram_BitFieldType reserved: 23;
    } data;
} Mram_Axi2tmcWrCfgType;

/** @brief AXI2TMC write/read interval configure register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType cdCycle: 30;
        Mram_BitFieldType mode: 2;
    } data;
} Mram_Axi2tmcIntervalType;

/** @brief AXI2TMC write abort configure register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType maxCnt: 8;
        Mram_BitFieldType reserved1: 8;
        Mram_BitFieldType sfty1: 1;
        Mram_BitFieldType sfty2: 1;
        Mram_BitFieldType sfty3: 1;
        Mram_BitFieldType sfty4: 1;
        Mram_BitFieldType reserved2: 11;
        Mram_BitFieldType rflct: 1;
    } data;
} Mram_Axi2tmcWrAbortType;

/** @brief AXI2TMC write buffer configure register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType entry: 7;
        Mram_BitFieldType reserved1: 1;
        Mram_BitFieldType wmlL: 7;
        Mram_BitFieldType reserved2: 1;
        Mram_BitFieldType wmlG: 7;
        Mram_BitFieldType reserved3: 3;
        Mram_BitFieldType wrbufEntryMode: 1;
        Mram_BitFieldType flushMode: 1;
        Mram_BitFieldType flushDisAw: 1;
        Mram_BitFieldType full: 1;
        Mram_BitFieldType empty: 1;
        Mram_BitFieldType flush: 1;
    } data;
}  Mram_Axi2tmcWrbufCfgType;

/** @brief AXI2TMC read configure register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType rdOtDis: 1;
        Mram_BitFieldType rdRespEn: 1;
        Mram_BitFieldType rdAddrNxtDis: 1;
        Mram_BitFieldType rdSyncEn: 1;
        Mram_BitFieldType rdbufClr: 1;
        Mram_BitFieldType rdbufRoundMode: 1;
        Mram_BitFieldType reserved: 26;
    } data;
} Mram_Axi2tmcRdCfgType;

/** @brief AXI2TMC read buffer configure0 register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType rdbufEn: 1;
        Mram_BitFieldType reserved: 15;
        Mram_BitFieldType mid: 5;
        Mram_BitFieldType arid4To7: 8;
        Mram_BitFieldType masterIdentReserved: 3;
    } data;
} Mram_Axi2tmcRdbufCfg0Type;

/** @brief AXI2TMC read buffer configure1 register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType masterIdentDis: 1;
        Mram_BitFieldType fixedRdTrigDis: 1;
        Mram_BitFieldType incrRdTrigDis: 1;
        Mram_BitFieldType wrapRdTrigDis: 1;
        Mram_BitFieldType shareDis: 1;
        Mram_BitFieldType splitDis: 1;
        Mram_BitFieldType reserved1: 2;
        Mram_BitFieldType mimBurstWord: 4;
        Mram_BitFieldType maxBurstWord: 4;
        Mram_BitFieldType midMask: 5;
        Mram_BitFieldType arid4To7Mask: 8;
        Mram_BitFieldType masterIdentEnReserved: 3;
    } data;
} Mram_Axi2tmcRdbufCfg1Type;

/** @brief AXI2TMC read buffer status register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType prefetchBaseAddr: 24;
        Mram_BitFieldType chEntryRdy: 8;
    } data;
} Mram_Axi2tmcRdbufStaType;

/** @brief AXI2TMC arbit configure register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType maxHoldCnt: 24;
        Mram_BitFieldType reserved: 6;
        Mram_BitFieldType clrHoldCnt: 1;
        Mram_BitFieldType rwPriority: 1;
    } data;
} Mram_Axi2tmcArbitCfgType;

/** @brief APB2TMC configure register */
typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType yAddress: 5;
        Mram_BitFieldType xAddr: 10;
        Mram_BitFieldType bAddress: 3;
        Mram_BitFieldType ifren1: 1;
        Mram_BitFieldType ifren: 1;
        Mram_BitFieldType apb2tmcCmd: 5;
        Mram_BitFieldType reserved1: 2;
        Mram_BitFieldType apb2tmcDataUpdDis: 1;
        Mram_BitFieldType apb2tmcCmdByp: 1;
        Mram_BitFieldType apb2tmcLockSta: 1;    /* RO */
        Mram_BitFieldType apb2tmcLockReq: 1;
        Mram_BitFieldType apb2tmcTrig: 1;
    } data;
} Mram_Apb2tmcCfgType;

/** @brief APB2TMC Data register */
typedef union {
    uint32_t reg[MRAM_APB2TMC_DATA_NUM];
    struct {
        Mram_BitFieldType data0To127[MRAM_APB2TMC_DATA_NUM - 1u];
        Mram_BitFieldType data128To146: 19;
        Mram_BitFieldType reserved: 13;
    } data;
} Mram_Apb2tmcDataType;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType rrdis: 1;
        Mram_BitFieldType rrlutLoadTrig: 1;
        Mram_BitFieldType rrlutRdy: 1;
        Mram_BitFieldType rrldFromInfo1: 1;
        Mram_BitFieldType rrlutEccDis: 1;
        Mram_BitFieldType reserved: 22;
        Mram_BitFieldType powrpSplitEn: 1;
        Mram_BitFieldType tmb2rrmainEn: 1;
        Mram_BitFieldType tmb2rrinfoEn: 1;
        Mram_BitFieldType tsmcRrImprove: 1;
        Mram_BitFieldType tsmcRrEn: 1;
    } data;
} Mram_RowRepCtrlType;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType tmbRegAddr: 6;
        Mram_BitFieldType tmbRegAccType: 1;
        Mram_BitFieldType tmbRegAccTrig: 1;
        Mram_BitFieldType tmbRegAccEn: 1;
        Mram_BitFieldType reserved: 20;
        Mram_BitFieldType tap2bistCdcFix_n: 1;
        Mram_BitFieldType bistBusy: 1;
        Mram_BitFieldType bistEn: 1;
    } data;
} Mram_Apb2tmbCtrlType;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType data31To0;
    } data;
} Mram_TmbRegData31To0Type;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType data39To32: 8;
        Mram_BitFieldType reserved: 24;
    } data;
} Mram_TmbRegData39To32Type;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType value: 24;
        Mram_BitFieldType reserved: 8;
    } data;
} Mram_MramcStuckThreshType;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType reserved1: 1;
        Mram_BitFieldType tmcWkup_timingRegEn: 1;
        Mram_BitFieldType extTmcTimingRegEn: 1;
        Mram_BitFieldType sramDataSrcSel: 1;
        Mram_BitFieldType tmcEccFixDis: 1;
        Mram_BitFieldType autoRunWakeupDis: 1;
        Mram_BitFieldType reserved2: 26;
    } data;
} Mram_TmcExtraCtrlType;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType tcycrd: 4;
        Mram_BitFieldType twk: 7;
        Mram_BitFieldType twkRide32En: 1;
        Mram_BitFieldType trds: 8;
        Mram_BitFieldType trdsEn: 1;
        Mram_BitFieldType reserved1: 4;
        Mram_BitFieldType trdl: 7;
    } data;
} Mram_ExtTmcRtimer1Type;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType tr2wTwrs: 7;
        Mram_BitFieldType tr2wTwrsRide16En: 1;
        Mram_BitFieldType tw2r: 7;
        Mram_BitFieldType tw2rRide16En: 1;
        Mram_BitFieldType reserved1: 8;
        Mram_BitFieldType tpgm: 8;
    } data;
} Mram_ExtTmcRtimer0Type;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType startAddr: 22;
        Mram_BitFieldType reserved: 10;
    } data;
} Mram_ImgCrcRgnAddrType;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType imgSize: 22;
        Mram_BitFieldType reserved: 10;
    } data;
} Mram_ImgCrcRgnSizeType;


typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType valid: 1;
        Mram_BitFieldType reserved: 30;
    } data;
} Mram_ImgCrcRgnVldType;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType crcSingTrig: 1;
        Mram_BitFieldType crcLoopTrig: 1;
        Mram_BitFieldType crcPass: 1;
        Mram_BitFieldType crcFail: 1;
        Mram_BitFieldType crcDone: 1;
        Mram_BitFieldType reserved1: 3;
        Mram_BitFieldType crcDoneCnt: 8;
        Mram_BitFieldType reserved2: 16;
    } data;
} Mram_ImgCrcRgnCfgType;


typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType size: 8;
        Mram_BitFieldType reserved: 16;
        Mram_BitFieldType mramVersion: 8;
    } data;
} Mram_MramSizeType;

typedef union {
    uint32_t reg;
    struct {
        Mram_BitFieldType phaseRevision: 8;
        Mram_BitFieldType minorRevision: 8;
        Mram_BitFieldType majorRevision: 8;
        Mram_BitFieldType reserve: 8;
    } data;
} Mram_RevisionType;

/**
 *  Structure of controller parameters and runtime variables
 */

typedef enum {
    MRAM_TMC_CMD_IDLE         = 0u,
    MRAM_TMC_CMD_READ         = 1u,
    MRAM_TMC_CMD_LOAD         = 2u,
    MRAM_TMC_CMD_WRITE        = 3u,
    MRAM_TMC_CMD_CLR_LOAD     = 4u,
    MRAM_TMC_CMD_WRT_CONFIG   = 5u,
    MRAM_TMC_CMD_READ_CONFIG  = 6u,
    MRAM_TMC_CMD_C_RUN_DMA    = 7u,
    MRAM_TMC_CMD_SLP          = 11u,
    MRAM_TMC_CMD_WAKEUP       = 12u,
    MRAM_TMC_CMD_AUTO_WAKEUP  = 13u,
    MRAM_TMC_CMD_WRITE_2CPB   = 14u,
    MRAM_TMC_CMD_WRITE_OTP    = 15u,
    MRAM_TMC_CMD_RECALL       = 22u,
    MRAM_TMC_CMD_WRITE_SRAM   = 23u,
    MRAM_TMC_CMD_READ_SRAM    = 24u,
    MRAM_TMC_CMD_SET_TRIM     = 25u,
    MRAM_TMC_CMD_READ_TRIM    = 26u,
} Mram_TmcCmdCodeType;

typedef enum {
    MRAM_TMC_CMD_REWRITE_RESERVED = 0,
    MRAM_TMC_CMD_REWRITE_DISABLE_BY_55AA = 1u,
    MRAM_TMC_CMD_REWRITE_ALWAYS_DISABLE = 2u,
    MRAM_TMC_CMD_REWRITE_ALWAYS_ENABLE = 3u
} Mram_RefCellRewriteModeType;

typedef struct {
    Mram_MramcFuncInt0Type int0;
    Mram_MramcFuncInt1Type int1;
} Mram_IntFlagType;

/***************************************************************************************************
 *                                  Private Variable Definitions                                   *
 **************************************************************************************************/


#ifdef CFG_PLATFORM_MCAL
#define EEP_START_SEC_CODE
#include "Eep_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

/***************************************************************************************************
 *                                  Private Function Declarations                                  *
 **************************************************************************************************/
/********************************************************************************************************
 * \brief This function Wait for some bits of a register to be set or cleared.
 *
 * \verbatim
 * Syntax             : int32_t Mram_WaitForBitTimes(uint32_t reg, const uint32_t mask,
 *                              bool clear, uint32_t times)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : reg - Polled register address
 *                      mask - Bits of concern
 *                      clear - Whether or not you are concerned about clearing
 *                      times - Number of polling timeouts
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK: Wait successful
 *                      MRAM_RET_FAILED: Wait timeout
 *
 * Description        : The value of a variable register until it reaches the expected value or times out
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_WaitForBitTimes(uint32_t reg, const uint32_t mask,
                                        bool clear, uint32_t times) {
    uint32_t val;
    uint32_t count = 0;
    int32_t ret = MRAM_RET_FAILED;

    while (count < times) {
        val = readl(reg);
        if (clear == true) {
            val = ~val;
        }
        val &= mask;
        if (val == mask) {
            ret = MRAM_RET_OK;
            break;
        }
        count++;
    }
    MRAM_LOG_DEBUG("wait bit(0x%x, %d, 0x%x), r(0x%08x)\r\n", reg, clear, mask, readl(reg));
    return ret;
}

/********************************************************************************************************
 * \brief This function write mramc apb register.
 *
 * \verbatim
 * Syntax             : void Mram_ApbWritel(Mram_HostType *host, uint32_t addr,
 *                          uint32_t value)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      addr - mram apb register address
 *                      value - mram apb register value
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : The physical address of the register is automatically calculated from the
 *                      information in the handle
 * \endverbatim
 *******************************************************************************************************/
static void Mram_ApbWritel(Mram_HostType *host, uint32_t addr,
                             uint32_t value)
{
    writel(value, host->config.apbBase + addr);

    MRAM_LOG_DEBUG("w(0x%x, 0x%08x), r(0x%08x)\r\n", addr, value,
                readl(host->config.apbBase + addr));
}

/********************************************************************************************************
 * \brief This function read the value mramc apb register.
 *
 * \verbatim
 * Syntax             : uint32_t Mram_ApbReadl(Mram_HostType *host, uint32_t addr)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      addr - mram apb register address
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : mram apb register value
 *
 * Description        : The physical address of the register is automatically calculated from the
 *                      information in the handle
 * \endverbatim
 *******************************************************************************************************/
static uint32_t Mram_ApbReadl(Mram_HostType *host, uint32_t addr)
{
    MRAM_LOG_DEBUG("r(0x%x, r(0x%08x)\r\n", addr, readl(host->config.apbBase + addr));
    return readl(host->config.apbBase + addr);

}

/********************************************************************************************************
 * \brief This function write mram apb register bits.
 *
 * \verbatim
 * Syntax             : void Mram_ApbWriteBits(Mram_HostType *host, uint32_t addr,
 *                              uint8_t startBit, uint8_t width, uint32_t val)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      addr - mram apb register address
 *                      startBit - Start bit to be written to
 *                      width - Ready to write width
 *                      val - The value ready to be written
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : The physical address of the register is automatically calculated from the
 *                      information in the handle; The start bit starts from 0 and reaches a maximum
 *                      of 31
 * \endverbatim
 *******************************************************************************************************/
static void Mram_ApbWriteBits(Mram_HostType *host, uint32_t addr,
                                 uint8_t startBit, uint8_t width, uint32_t val)
{
    RMWREG32(host->config.apbBase + addr, startBit, width, val & (((uint32_t)1u<<(width)) - 1u));
    MRAM_LOG_DEBUG("wb(0x%x, %d %d ,0x%08x), r(0x%08x)\r\n", addr,
                startBit, width, val << startBit, readl(host->config.apbBase + addr));
}

/********************************************************************************************************
 * \brief This function Wait for the Mram controller to be ready.
 *
 * \verbatim
 * Syntax             : int32_t Mram_WaitReady(Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Mram is ready to be completed
 *                      Others - Wait timeout
 *
 * Description        : Common timeouts can be caused by not having the required resources ready,
 *                      such as Resetgen or clockgen
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_WaitReady(Mram_HostType *host)
{
    /* Poll until mramReady=1,Indicates mram controller is ready */
    return Mram_WaitForBitTimes(host->config.apbBase + MRAM_MRAMC_STA,
        MRAM_MRAMC_STA_MRAM_READY_MASK, false, MRAM_MAX_POLLING_MRAM_READY_TIMEOUT);
}

/********************************************************************************************************
 * \brief This function Disable the current interrupt triggering function.
 *
 * \verbatim
 * Syntax             : void Mram_DisableIrq(Mram_HostType *host, Mram_IntFlagType *flag)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : flag - Return the current trigger switch status
 *
 * Return value       : None
 *
 * Description        : The parameter cannot be null
 * \endverbatim
 *******************************************************************************************************/
static void Mram_DisableIrq(Mram_HostType *host, Mram_IntFlagType *flag)
{
    /* Turn off only the trigger switch of the interrupt, and the interrupt status can still be viewed */
    flag->int0.reg = Mram_ApbReadl(host, MRAM_MRAMC_FUNC_INT0_SIG_EN);
    flag->int1.reg = Mram_ApbReadl(host, MRAM_MRAMC_FUNC_INT1_SIG_EN);
    Mram_ApbWritel(host, MRAM_MRAMC_FUNC_INT0_SIG_EN, 0x0u);
    Mram_ApbWritel(host, MRAM_MRAMC_FUNC_INT1_SIG_EN, 0x0u);
}

/********************************************************************************************************
 * \brief This function Restores the triggering of the current interrupt by parameter.
 *
 * \verbatim
 * Syntax             : void Mram_EnableIrq(Mram_HostType *host, Mram_IntFlagType *flag)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      flag -  The current trigger switch status
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : The parameter cannot be null
 * \endverbatim
 *******************************************************************************************************/
static void Mram_EnableIrq(Mram_HostType *host, Mram_IntFlagType *flag)
{
    Mram_ApbWritel(host, MRAM_MRAMC_FUNC_INT0_SIG_EN, flag->int0.reg);
    Mram_ApbWritel(host, MRAM_MRAMC_FUNC_INT1_SIG_EN, flag->int1.reg);
}

/********************************************************************************************************
 * \brief This function Wait for TMC to prepare until time out.
 *
 * \verbatim
 * Syntax             : int32_t Mram_WaitTmcIdle(Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - The TMC is idle.
 *                      Others - TMC is busy
 *
 * Description        : This status does not need to be concerned when accessing Mainblock, and
 *                      only needs to be concerned when sending TMC commands
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_WaitTmcIdle(Mram_HostType *host)
{
    /* Poll until tmcBusy=0,Indicates that the tmc interface is idle */
    return Mram_WaitForBitTimes(host->config.apbBase + MRAM_MRAMC_STA,
        MRAM_MRAMC_STA_TMC_BUSY, true, MRAM_MAX_POLLING_TMC_BUSY_TIMEOUT);
}

/********************************************************************************************************
 * \brief This function Wait until the tmc command is sent and the tmc status changes to ready.
 *
 * \verbatim
 * Syntax             : int32_t Mram_Apb2tmcWaitTrigDone(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - The TMC is idle, and triggering succeeds.
 *                      MRAM_RET_TIMEOUT - TMC triggering failed, Or timeout
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_Apb2tmcWaitTrigDone(Mram_HostType *host)
{
    int32_t ret = MRAM_RET_TIMEOUT;
    /* Poll until apb2tmcTrig=0,Indicates that the tmcCmd has sent complete */
    if (MRAM_RET_OK != Mram_WaitForBitTimes(host->config.apbBase + MRAM_APB2TMC_CFG,
        MRAM_APB2TMC_CFG_TRIG_MASK, true, MRAM_MAX_POLLING_APB2TMC_TRIGE_TIMEOUT)) {
        MRAM_LOG_ERROR("Polling tmc trig failed\r\n");
    /* Wait until the tmc interface is ready */
    } else if (MRAM_RET_OK != Mram_WaitTmcIdle(host)) {
        MRAM_LOG_ERROR("Polling tmc busy failed\r\n");
    } else {
        ret = MRAM_RET_OK;
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function try to enable/disable AXI write stall.
 *
 * \verbatim
 * Syntax             : int32_t Mram_WriteStall(Mram_HostType *host,
 *                          bool enable)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      enable - The switch configuration of write stall
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Write stall configuration complete
 *                      Others - Configuration failure
 *
 * Description        : Enable and disable must be used in pairs; After this function is enabled,
 *                      write operations are prohibited.
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_WriteStall(Mram_HostType *host, bool enable)
{
    int32_t ret = MRAM_RET_OK;
    /* Configuring wrstall */
    Mram_ApbWriteBits(host, MRAM_AXI2TMC_WR_CFG,
        MRAM_AXI2TMC_WR_CFG_WR_STALL_LSB, 1u, (true == enable) ? 1u : 0u);

    if (true == enable) {
        /* Poll until wrStallRdy=1,Indicates that the write stall has been enable */
        ret = Mram_WaitForBitTimes(host->config.apbBase + MRAM_AXI2TMC_WR_CFG,
            MRAM_AXI2TMC_WR_CFG_WR_STALL_RDY_MASK, false, MRAM_MAX_POLLING_WR_STALL_TIMEOUT);
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function Set the frequency division coefficient of tmc.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcClkDivSetup(Mram_HostType *host,
 *                                  uint8_t divNum)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      divNum - Frequency division coefficient
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Configuration successful.
 *                      Others - The configuration does not take effect.
 *
 * Description        : The calculation formula of frequency division: tmc_clk_freq =
 *                      aclk_freq / (div_num[3:0]+1).
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcClkDivSetup(Mram_HostType *host, uint8_t divNum)
{
    /* Configure the frequency division register */
    Mram_ApbWriteBits(host, MRAM_MRAMC_GLB_CTRL0,
        MRAM_MRAMC_GLB_CTRL0_TMC_CLK_DIV_NUM_LSB, 4u, divNum);
    /* Need to wait for the update of frequency division coefficient */
    return Mram_WaitForBitTimes(host->config.apbBase + MRAM_MRAMC_GLB_CTRL0,
        MRAM_MRAMC_GLB_CTRL0_TMC_CLK_UPD_BUSY_MASK, true, MRAM_MAX_POLLING_TMC_CLK_UPD_TIMEOUT);
}

/********************************************************************************************************
 * \brief This function configure read/write timing between TMC and MRAM macro.
 *
 * \verbatim
 * Syntax             : int32_t Mram_ExtTmcRtimerSetup(Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Configuration successful.
 *                      Others - An unexpected configuration error occurred.
 *
 * Description        : The default APB register value is for tmc_clk frequency 50MHz
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_ExtTmcRtimerSetup(Mram_HostType *host)
{
    Mram_TmcExtraCtrlType tmcExtraCtrl;
    Mram_ExtTmcRtimer1Type extTmcRtimer1;
    Mram_ExtTmcRtimer0Type extTmcRtimer0;
    uint32_t temp;

    extTmcRtimer1.reg = Mram_ApbReadl(host, MRAM_EXT_TMC_RTIMER1);
    extTmcRtimer0.reg = Mram_ApbReadl(host, MRAM_EXT_TMC_RTIMER0);

    /* Trdl > (Min_Trdl = 200ns(MRAM_MIN_TRDL_NS))
    Trdl = tmc_clk_period * (extTmcRtimer1.data.trdl+1) */
    extTmcRtimer1.data.trdl = MRAM_DIV_ROUND_UP(
        MRAM_MIN_TRDL_NS * ( host->tmcClkHz / 1000000u), 1000u) - 1u;
    /* trdsEn must be set as 1 */
    extTmcRtimer1.data.trdsEn = 1u;
    /* Trds > (Min_Trds = 100ns(MRAM_MIN_TRDS_NS))
    Trds = tmc_clk_period * (extTmcRtimer1.data.trds+1) */
    extTmcRtimer1.data.trds = MRAM_DIV_ROUND_UP(
        MRAM_MIN_TRDS_NS * ( host->tmcClkHz / 1000000u), 1000u) - 1u;
    /* Twk > (Min_Twk = 4000ns(MRAM_MIN_TWK_NS))
    When extTmcRtimer1.data.twkRide32En = 0, Twk = tmc_clk_period * (extTmcRtimer1.data.twk+1)
    When extTmcRtimer1.data.twkRide32En = 1, Twk = tmc_clk_period * (extTmcRtimer1.data.twk+1) * 32 */
    temp = MRAM_DIV_ROUND_UP(MRAM_MIN_TWK_NS * ( host->tmcClkHz / 1000000u), 1000u);
    if (temp > 0x7fu) {
        extTmcRtimer1.data.twkRide32En = 1u;
        temp /= 32u;
    }
    extTmcRtimer1.data.twk = temp - 1u;
    /* Tcycrd > (Min_Tcycrd = 18ns(MRAM_32M_MIN_TCYCRD_NS) for MRAM32Mb, or 13ns(MRAM_4M_MIN_TCYCRD_NS) for MRAM4Mb)
    Tcycrd = tmc_clk_period * (extTmcRtimer1.data.tcycrd+1) */
    extTmcRtimer1.data.tcycrd = MRAM_DIV_ROUND_UP(
        ((MRAM_TYPE_32M == host->info.mramVersion) ?
            MRAM_32M_MIN_TCYCRD_NS : MRAM_4M_MIN_TCYCRD_NS) * ( host->tmcClkHz / 1000000u),
            1000u) - 1u;
    /* (Min_Tpgm = 225ns) < Tpgm < (Max_Tpgm = 275ns)
    MRAM_MID_TPGM_NS = (Min_Tpgm + Max_Tpgm) / 2
    Tpgm = tmc_clk_period * (extTmcRtimer0.data.tpgm+1) */
    extTmcRtimer0.data.tpgm = MRAM_DIV_ROUND_UP(
        MRAM_MID_TPGM_NS * ( host->tmcClkHz / 1000000u), 1000u) - 1u;
    /*Tw2r > (Min_Tw2r = 1700ns(MRAM_32M_MIN_TW2R_NS) for MRAM32Mb, or 1000ns(MRAM_4M_MIN_TW2R_NS) for MRAM4Mb)
    When extTmcRtimer0.data.tw2rRide16En = 0, Tw2r = tmc_clk_period * (extTmcRtimer0.data.tw2r+1)
    When extTmcRtimer0.data.tw2rRide16En = 1, Tw2r = tmc_clk_period * (extTmcRtimer0.data.tw2r+1) * 16 */
    temp = MRAM_DIV_ROUND_UP(
            ((MRAM_TYPE_32M == host->info.mramVersion) ?
                MRAM_32M_MIN_TW2R_NS : MRAM_4M_MIN_TW2R_NS) * ( host->tmcClkHz / 1000000u),
                1000u);
    if (temp > 0x7fu) {
        extTmcRtimer0.data.tw2rRide16En = 1u;
        temp /= 16u;
    }
    extTmcRtimer0.data.tw2r = temp - 1u;
    /* Tr2w/Twrs > (Min_Tr2w/Min_Twrs = 3000ns(MRAM_MIN_TR2W_NS))
    When extTmcRtimer0.data.tr2wTwrsRide16En = 0, Tr2w/Twrs = tmc_clk_period * (extTmcRtimer0.data.tr2wTwrs+1)
    When extTmcRtimer0.data.tr2wTwrsRide16En = 1, Tr2w/Twrs = tmc_clk_period * (extTmcRtimer0.data.tr2wTwrs+1) * 16*/
    temp = MRAM_DIV_ROUND_UP(MRAM_MIN_TR2W_NS * ( host->tmcClkHz / 1000000u), 1000u);
    if (temp > 0x7fu) {
        extTmcRtimer0.data.tr2wTwrsRide16En = 1u;
        temp /= 16u;
    }
    extTmcRtimer0.data.tr2wTwrs = temp - 1u;

    // The following configurations are from the "TRM:21.5.9.3 Run time clock Frequency Change"
    /* Registers tmcRtimer are adjusted to be writable */
    tmcExtraCtrl.reg = Mram_ApbReadl(host, MRAM_TMC_EXTRA_CTRL);
    tmcExtraCtrl.data.extTmcTimingRegEn = 1u;
    Mram_ApbWritel(host, MRAM_TMC_EXTRA_CTRL, tmcExtraCtrl.reg);
    /* Adjusted tmcRtimer when clock change in run time */
    Mram_ApbWritel(host, MRAM_EXT_TMC_RTIMER0, extTmcRtimer0.reg);
    Mram_ApbWritel(host, MRAM_EXT_TMC_RTIMER1, extTmcRtimer1.reg);

    return Mram_WaitTmcIdle(host);
}

/********************************************************************************************************
 * \brief This function Calculate the current tmc clock frequency based on the frequency division configuration.
 *
 * \verbatim
 * Syntax             : uint32_t Mram_TmcClkGet(Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Current tmc clock frequency
 *
 * Description        : The aclk in the device handle must be configured accurately.
 * \endverbatim
 *******************************************************************************************************/
static uint32_t Mram_TmcClkGet(Mram_HostType *host)
{
    Mram_MramcGlbCtrl0Type mramcGlbCtrl0;
    mramcGlbCtrl0.reg = Mram_ApbReadl(host, MRAM_MRAMC_GLB_CTRL0);
    return host->aclkHz / ((uint32_t)(mramcGlbCtrl0.data.tmcClkDivNum) + 1u);
}

/********************************************************************************************************
 * \brief This function Trigger the sequence for Autowakeup.
 *
 * \verbatim
 * Syntax             : int32_t Mram_AutoWakeup(Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Triggering the wakeup command succeeded
 *                      Others - Trigger failure
 *
 * Description        : During the sequence, MRAM Controller will automatically issue TMC_CMD_WAKEUP
 *                      to TMC, then read some important trim information from MRAM, or write ref-cell
 *                      rewrite flag 0x55aa to MRAM if rewrite sequence is performed.
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_AutoWakeup(Mram_HostType *host)
{
    Mram_Apb2tmcCfgType apb2tmcCfg;
    apb2tmcCfg.reg = 0u;

    // The following configurations are from the "TRM:21.5.9.2.1 Know About MRAM Trim-AUOTO_WAKE_UP"
    /* Software manually sends wake-up commands */
    apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_AUTO_WAKEUP;
    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
    apb2tmcCfg.data.apb2tmcTrig = 1;
    /* Triger start */
    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);

    /* Wait for mramc read some important trim information from MRAM, or rewrite complete */
    return Mram_Apb2tmcWaitTrigDone(host);
}

/********************************************************************************************************
 * \brief This function Trigger the sequence for RRLUT_LOAD.
 *
 * \verbatim
 * Syntax             : Mram_RrlutSwLoad(Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Triggering succeeded
 *                      Others - Trigger failure
 *
 * Description        : During the sequence, MRAM Controller will automatically read some important
 *                      trim information from MRAM
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_RrlutSwLoad(Mram_HostType *host)
{
    Mram_RowRepCtrlType rowRepairCtrl;

    // The following configurations are from the "TRM:21.5.9.2.1 Know About MRAM Trim-RRLUT_LOAD"
    rowRepairCtrl.reg = Mram_ApbReadl(host, MRAM_ROW_REPCTRL);
    rowRepairCtrl.data.rrlutLoadTrig = 1u;
    /* Triger start */
    Mram_ApbWritel(host, MRAM_ROW_REPCTRL, rowRepairCtrl.reg);

    /* Wait for the RRLUT trigger to complete */
    return Mram_WaitForBitTimes(
        host->config.apbBase + MRAM_ROW_REPCTRL,
        MRAM_ROW_REPCTRL_RRLUT_LOAD_TRIG_MASK, true,
        MRAM_MAX_POLLING_RRLUT_LOAD_TRIG_TIMEOUT);
}

/********************************************************************************************************
 * \brief This function Set the write cooling time.
 *
 * \verbatim
 * Syntax             : void Mram_WrIntervalSetup(
 *                              Mram_HostType *host, uint8_t wrIntervalUs)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      wrIntervalUs - Cooling time unit us
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : The cooldown time cannot be greater than the stuck time. The cooldown
 *                      time is automatically increased by 200us to form the stuck time
 * \endverbatim
 *******************************************************************************************************/
static void Mram_WrIntervalSetup(Mram_HostType *host, uint8_t wrIntervalUs)
{
    Mram_Axi2tmcIntervalType wrInterval;
    Mram_MramcStuckThreshType stuckThresh;
    uint32_t tempValue;

    /* Set the write cooling time */
    wrInterval.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_WR_INTERVAL);
    wrInterval.data.cdCycle = MRAM_US_2_TICKS(host->tmcClkHz, wrIntervalUs);
    Mram_ApbWritel(host, MRAM_AXI2TMC_WR_INTERVAL, wrInterval.reg);

    /* Reconfiguring stuck must take longer than the write cooldown,
    otherwise a stuck error will be triggered during the write cooldown */
    tempValue = MRAM_US_2_TICKS(host->tmcClkHz, (uint32_t)wrIntervalUs + 200u);
    stuckThresh.reg = Mram_ApbReadl(host, MRAM_MRAMC_STUCK_THRESH);
    if (tempValue > stuckThresh.data.value) {
        stuckThresh.data.value = tempValue;
        Mram_ApbWritel(host, MRAM_MRAMC_STUCK_THRESH, stuckThresh.reg);
    }
    host->wrIntervalUs = wrIntervalUs;
}

/********************************************************************************************************
 * \brief This function Turn on or off the feature to wake up automatically.
 *
 * \verbatim
 * Syntax             : void Mram_RuntimeWakeupSetup(
 *                              Mram_HostType *host, bool autoEn)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      autoEn - Automatic wake up switch
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Set the register as if there would be no failure. wakeup MRAM Controller
 *                      from sleep mode / deep sleep mode to standby / low power standby mode when
 *                      reset of MRAM Controller is already released.
 * \endverbatim
 *******************************************************************************************************/
static void Mram_RuntimeWakeupSetup(Mram_HostType *host, bool autoEn)
{
    Mram_TmcExtraCtrlType tmcExtraCtrl;

    tmcExtraCtrl.reg = Mram_ApbReadl(host, MRAM_TMC_EXTRA_CTRL);
    tmcExtraCtrl.data.autoRunWakeupDis = (true == autoEn) ? 0u : 1u;
    Mram_ApbWritel(host, MRAM_TMC_EXTRA_CTRL, tmcExtraCtrl.reg);
}

/********************************************************************************************************
 * \brief This function Configure the channel of the readbuffer.
 *
 * \verbatim
 * Syntax             : int32_t Mram_RdbufferChannelConfig(
 *                          Mram_HostType *host, const Mram_RdBufferCfgType* rdbufferCfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      rdbufferCfg - Configuration parameter
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Configuration successful
 *                      Others - Configuration failure
 *
 * Description        : The channel ID cannot exceed the upper limit(5), You can specify a channel
 *                      user or not
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_RdbufferChannelConfig(Mram_HostType *host, const Mram_RdBufferCfgType* rdbufferCfg)
{
    Mram_Axi2tmcRdbufCfg0Type cfg0;
    Mram_Axi2tmcRdbufCfg1Type cfg1;
    int32_t cfgRet;

    if (rdbufferCfg->channelId >= MRAM_RDBUFFER_NUM) {
        cfgRet = MRAM_RET_PARAMETER_ERR;
    }
    else {
        cfg0.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_RDBUF_CFG0 + (0x10u * ((uint32_t)(rdbufferCfg->channelId))));
        cfg1.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_RDBUF_CFG1 + (0x10u * ((uint32_t)(rdbufferCfg->channelId))));

        /* Close the channel before configuring it */
        cfg0.data.rdbufEn = 0;
        Mram_ApbWritel(host, MRAM_AXI2TMC_RDBUF_CFG0 + (0x10u * ((uint32_t)(rdbufferCfg->channelId))), cfg0.reg);

        if (rdbufferCfg->enable == true) {
            if ((rdbufferCfg->masterId > MRAM_AXI2TMC_RDBUF_CFG0_MASTER_ID_MASK)
                && (rdbufferCfg->arid > MRAM_AXI2TMC_RDBUF_CFG0_AXI_ID_MASK)) {
                /* for any other unspecified master. */
                cfg1.data.masterIdentDis = 1u;
            }
            else {
                /* for specified master, or configure */
                cfg1.data.masterIdentDis = 0u;
                if (rdbufferCfg->masterId > MRAM_AXI2TMC_RDBUF_CFG0_MASTER_ID_MASK) {
                    /* Ignore master id */
                    cfg1.data.midMask = 0u;
                }
                else {
                    /* Configure the channel mid */
                    cfg0.data.mid = rdbufferCfg->masterId & MRAM_AXI2TMC_RDBUF_CFG0_MASTER_ID_MASK;
                    cfg1.data.midMask = MRAM_AXI2TMC_RDBUF_CFG0_MASTER_ID_MASK;
                }

                if (rdbufferCfg->arid > MRAM_AXI2TMC_RDBUF_CFG0_AXI_ID_MASK) {
                    /* Ignore arid[7:4] */
                    cfg1.data.arid4To7Mask = 0u;
                }
                else {
                    /* Configure the channel arid, Focus only on bit[7:4] */
                    cfg0.data.arid4To7 = rdbufferCfg->arid & MRAM_AXI2TMC_RDBUF_CFG0_AXI_ID_MASK;
                    cfg1.data.arid4To7Mask = MRAM_AXI2TMC_RDBUF_CFG0_AXI_ID_MASK;
                }
            }
            /* Configure the channel and open it */
            cfg0.data.rdbufEn = 1u;
            Mram_ApbWritel(host, MRAM_AXI2TMC_RDBUF_CFG1 + (0x10u * ((uint32_t)(rdbufferCfg->channelId))), cfg1.reg);
            Mram_ApbWritel(host, MRAM_AXI2TMC_RDBUF_CFG0 + (0x10u * ((uint32_t)(rdbufferCfg->channelId))), cfg0.reg);
        }
        cfgRet = MRAM_RET_OK;
    }

    return cfgRet;
}

/********************************************************************************************************
 * \brief This function Stops triggering for crc checking.
 *
 * \verbatim
 * Syntax             : void Mram_CrcCheckStopTrigger(
 *                              Mram_HostType *host, uint8_t index)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      index - Specifies the index of the image region
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Stops triggering the current crc check and clears the current results
 * \endverbatim
 *******************************************************************************************************/
static void Mram_CrcCheckStopTrigger(Mram_HostType *host, uint8_t index)
{
    Mram_ImgCrcRgnCfgType rgnCfg;

    rgnCfg.reg = 0u;
    Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index), rgnCfg.reg);

    /* Clears the last result and status */
    rgnCfg.data.crcDoneCnt = 0;
    rgnCfg.data.crcDone = 1u;
    rgnCfg.data.crcFail = 1u;
    rgnCfg.data.crcPass = 1u;
    Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index), rgnCfg.reg);

    /* Clear the hardware Caculated CRC32 Result Value */
    Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_RSLT + (0x40u * (uint32_t)index), 0x0u);
}

/********************************************************************************************************
 * \brief This function Set the current image region to an invalid region.
 *
 * \verbatim
 * Syntax             : void  Mram_CrcCheckInvalid(
 *                              Mram_HostType *host, uint8_t index)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      index - Specifies the index of the image region
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static void  Mram_CrcCheckInvalid(Mram_HostType *host, uint8_t index)
{
    Mram_ImgCrcRgnVldType rgnVld;

    rgnVld.reg = 0u;
    Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_VLD + (0x40u * (uint32_t)index), rgnVld.reg);
}

/********************************************************************************************************
 * \brief This function  triggers crc checks through oneshot mode.
 *
 * \verbatim
 * Syntax             : int32_t Mram_CrcCheckOneshotTrigger(
 *                                  Mram_HostType *host, uint8_t index)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      index - Specifies the index of the image region
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - The trigger succeeds, and the crc check is correct
 *                      MRAM_RET_CRC_ERR - The trigger succeeded, but the crc check failed
 *                      Others - Trigger failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_CrcCheckOneshotTrigger(Mram_HostType *host, uint8_t index)
{
    int32_t ret = MRAM_RET_OK;
    Mram_ImgCrcRgnCfgType rgnCfg;
    Mram_ImgCrcRgnVldType rgnVld;
    Mram_ImgCrcRgnSizeType rgnSize;

    rgnVld.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_VLD + (0x40u * (uint32_t)index));
    /* Whether it is a valid channel */
    if (0u == rgnVld.data.valid) { /* Invalid channel */
        ret = MRAM_RET_FAILED;
        MRAM_LOG_ERROR("Rgn-%d is invalid\r\n", index);
    }
    else { /* Active channel */
        /* If the last trigger didn't end, try ending it first */
        Mram_CrcCheckStopTrigger(host, index);

        rgnCfg.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index));
        rgnCfg.data.crcSingTrig = 1u;
        Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index), rgnCfg.reg);

        /* Read the size of the channel image, which is used to calculate the theoretical duration of the timeout */
        rgnSize.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_SIZE + (0x40u * (uint32_t)index));
    }

    if (MRAM_RET_OK != ret) {
        /* Do nothing */
    }
    /* Wait for trigger to complete */
    else if (MRAM_RET_OK != Mram_WaitForBitTimes(
                host->config.apbBase + MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index),
                MRAM_IMG_CRC_RGN_CFG_CRC_SING_TRIG, true,
                MRAM_MAX_POLLING_CRC_TRIG_TIMEOUT(rgnSize.data.imgSize))) {
        /* Trigger failure */
        ret = MRAM_RET_TIMEOUT;
    }
    else {
        /* Trigger complete, read the result */
        rgnCfg.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index));
        if (rgnCfg.data.crcFail != 0u) {
            /* The last check result was failed. Procedure */
            ret = MRAM_RET_CRC_ERR;
            MRAM_LOG_ERROR("Rgn-%d crc no match 0x%x != 0x%x\r\n", index,
                Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_RSLT+ (0x40u * (uint32_t)index)),
                Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_EXPVAL+ (0x40u * (uint32_t)index)));
        }
        else {
            /* Do nothing */
        }
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function triggers crc checks through loop mode.
 *
 * \verbatim
 * Syntax             : int32_t Mram_CrcCheckLoopTrigger(
 *                                  Mram_HostType *host, uint8_t index)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      index - Specifies the index of the image region
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Trigger succeed
 *                      Others - Trigger failure
 *
 * Description        : In this mode, the controller will keep reading the data of the set area and
 *                      perform crc checks until the error occurs.The verification result is reported
 *                      asynchronously through an interrupt or obtained through an interface
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_CrcCheckLoopTrigger(Mram_HostType *host, uint8_t index)
{
    int32_t ret = MRAM_RET_OK;
    Mram_ImgCrcRgnCfgType rgnCfg;
    Mram_ImgCrcRgnVldType rgnVld;

    rgnVld.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_VLD + (0x40u * (uint32_t)index));
    /* Whether it is a valid channel */
    if (0u == rgnVld.data.valid) { /* Invalid channel */
        ret = MRAM_RET_FAILED;
        MRAM_LOG_ERROR("Rgn-%d is invalid\r\n", index);
    }
    else { /* Active channel */
        /* If the last trigger didn't end, try ending it first */
        Mram_CrcCheckStopTrigger(host, index);

        rgnCfg.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index));
        rgnCfg.data.crcLoopTrig = 1u;
        Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index), rgnCfg.reg);
        /* Relationship results do not need to be returned immediately after triggering */
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function fill the address according to the configuration for sending commands.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcFillMramWordAddr(
 *                                Mram_HostType *host, Mram_BlockType type,  uint32_t addr)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      type - Block type
 *                      addr - The address unit of a storage area is byte
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Fill successfully
 *                      MRAM_RET_PARAMETER_ERR - Address misalignment
 *                      MRAM_RET_UNSUPPORT - Unsupported block types
 *
 * Description        : The address needs to be 16B aligned.
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcFillMramWordAddr(Mram_HostType *host, Mram_BlockType type, uint32_t addr)
{
    int32_t ret = MRAM_RET_OK;
    uint32_t mramWordAddr;
    Mram_Apb2tmcCfgType apb2tmcCfg;

    /* Addresses need to be 16 aligned */
    if (!MRAM_IS_ALIGNED(addr, MRAM_WORD_SIZE_BYTE)) {
        ret = MRAM_RET_PARAMETER_ERR;
    }
    else {
        mramWordAddr = addr >> 4;  /* Dividing with MRAM_WORD_SIZE_BYTE */ ;
        apb2tmcCfg.reg = Mram_ApbReadl(host, MRAM_APB2TMC_CFG);

        switch (type) {
        case MRAM_BLOCK_TYPE_MAIN:
            apb2tmcCfg.data.ifren = 0;
            apb2tmcCfg.data.ifren1 = 0;
            apb2tmcCfg.data.yAddress = mramWordAddr & MRAM_APB2TMC_CFG_YADR_MASK;
            apb2tmcCfg.data.xAddr = (mramWordAddr >> MRAM_APB2TMC_CFG_XADR_LSB)
                                        & MRAM_APB2TMC_CFG_XADR_MASK;
            apb2tmcCfg.data.bAddress = (mramWordAddr >> MRAM_APB2TMC_CFG_BADR_LSB)
                                        & MRAM_APB2TMC_CFG_BADR_MASK;
            break;
        case MRAM_BLOCK_TYPE_INFO_USER:
            apb2tmcCfg.data.ifren = 1;
            apb2tmcCfg.data.ifren1 = 0;
            apb2tmcCfg.data.yAddress = mramWordAddr & MRAM_APB2TMC_CFG_YADR_MASK;
            apb2tmcCfg.data.xAddr = ((mramWordAddr >> MRAM_APB2TMC_CFG_XADR_LSB) & 0x1u) << 9;
            apb2tmcCfg.data.bAddress = (mramWordAddr >> 6) & MRAM_APB2TMC_CFG_BADR_MASK;
            break;
        case MRAM_BLOCK_TYPE_INFO_MARCO:
            apb2tmcCfg.data.ifren = 0;
            apb2tmcCfg.data.ifren1 = 1;
            apb2tmcCfg.data.yAddress = mramWordAddr & MRAM_APB2TMC_CFG_YADR_MASK;
            apb2tmcCfg.data.xAddr = 0;
            apb2tmcCfg.data.bAddress = (mramWordAddr >> 5) & MRAM_APB2TMC_CFG_BADR_MASK;
            break;
        case MRAM_BLOCK_TYPE_OTP_USER:
        case MRAM_BLOCK_TYPE_OTP_USER_2P2V:
            apb2tmcCfg.data.ifren = 1;
            apb2tmcCfg.data.ifren1 = 0;
            apb2tmcCfg.data.yAddress = mramWordAddr & MRAM_APB2TMC_CFG_YADR_MASK;
            apb2tmcCfg.data.xAddr = (MRAM_TYPE_32M == host->info.mramVersion) ? 1u : 5u;;
            apb2tmcCfg.data.bAddress = (mramWordAddr >> 5) & MRAM_APB2TMC_CFG_BADR_MASK;
            break;
        case MRAM_BLOCK_TYPE_OTP_MARCO:
        case MRAM_BLOCK_TYPE_OTP_MARCO_2P2V:
            apb2tmcCfg.data.ifren = 0;
            apb2tmcCfg.data.ifren1 = 1;
            apb2tmcCfg.data.yAddress = mramWordAddr & MRAM_APB2TMC_CFG_YADR_MASK;
            apb2tmcCfg.data.xAddr = 1;
            apb2tmcCfg.data.bAddress = (mramWordAddr >> 5) & MRAM_APB2TMC_CFG_BADR_MASK;
            break;
        default:
            /* Unsupported types */
            ret = MRAM_RET_UNSUPPORT;
            break;
        }

        if (MRAM_RET_OK == ret) {
            Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
        }
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function read mram word by apb.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcReadWord(
 *                              Mram_HostType *host, Mram_TmcRdWrWordCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : cfg - The configuration for read mram word, And provide the address of
 *                            buffer for read data
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Successful read
 *
 * Description        : Only one word(=16Bytes) can be read at a time.
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcReadWord(Mram_HostType *host, Mram_TmcRdWrWordCfgType *cfg)
{
    int32_t ret;
    Mram_Apb2tmcCfgType apb2tmcCfg;
    Mram_MramcGlbCtrl1Type glbCtrl1;
    uint32_t tempGlbCtrl1;
    uint8_t dataNum;

    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, 0x0u);

    if (MRAM_RET_OK != Mram_TmcFillMramWordAddr(host, cfg->type, cfg->addr)) {
        /* Failed to fill the address register due to parameter reasons */
        ret = MRAM_RET_PARAMETER_ERR;
    }
    else {
        /* Clear the data register beforehand */
        for (dataNum = 0u; dataNum < 6u; dataNum++) {
            Mram_ApbWritel(host, MRAM_APB2TMC_DATA + (4u * (uint32_t)dataNum), 0u);
        }
        glbCtrl1.reg = Mram_ApbReadl(host, MRAM_MRAMC_GLB_CTRL1);
        tempGlbCtrl1 = glbCtrl1.reg;
        if ((MRAM_BLOCK_TYPE_MAIN == cfg->type) ||
            (MRAM_BLOCK_TYPE_INFO_USER == cfg->type)) {
            glbCtrl1.data.tmcCfg2Cpb = 0;
        }
        else {
            glbCtrl1.data.tmcCfg2Cpb = 1;
        }
        Mram_ApbWritel(host, MRAM_MRAMC_GLB_CTRL1, glbCtrl1.reg);

        apb2tmcCfg.reg = Mram_ApbReadl(host, MRAM_APB2TMC_CFG);
        apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_READ;
        Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);

        apb2tmcCfg.data.apb2tmcTrig = 1;
        Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
        ret = Mram_Apb2tmcWaitTrigDone(host);
        if (MRAM_RET_OK == ret) {
            (void) Mram_HostMemcopy((void*)cfg->buffer,
                    (void*)(host->config.apbBase + MRAM_APB2TMC_DATA), 16u);
            cfg->info = Mram_ApbReadl(host, MRAM_APB2TMC_DATA_146_128);
        }

        /* Clear all touched register field to 0 is recommended. */
        Mram_ApbWritel(host, MRAM_MRAMC_GLB_CTRL1, tempGlbCtrl1);
        Mram_ApbWritel(host, MRAM_APB2TMC_CFG, 0x0u);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function write mram words by apb.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcWriteWord(
 *                          Mram_HostType *host, Mram_TmcRdWrWordCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      cfg -The configuration for write mram word, And provide the address of
 *                            buffer for write data
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcWriteWord(Mram_HostType *host, Mram_TmcRdWrWordCfgType *cfg)
{
    int32_t ret = MRAM_RET_OK;
    Mram_Apb2tmcCfgType apb2tmcCfg;
    uint32_t tempGlbCtrl1;
    Mram_MramcGlbCtrl1Type glbCtrl1;

    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, 0x0u);

    if (MRAM_RET_OK != Mram_WriteStall(host, true)) {
        ret = MRAM_RET_FAILED;
    }
    else if (MRAM_RET_OK != Mram_TmcFillMramWordAddr(host, cfg->type, cfg->addr)) {
        /* Failed to fill the address register due to parameter reasons */
        ret = MRAM_RET_PARAMETER_ERR;
    }
    else {
        glbCtrl1.reg = Mram_ApbReadl(host, MRAM_MRAMC_GLB_CTRL1);
        tempGlbCtrl1 = glbCtrl1.reg;
        apb2tmcCfg.reg = Mram_ApbReadl(host, MRAM_APB2TMC_CFG);
        apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_LOAD;
        /* First copy the 16B data into the register */
        (void) Mram_HostMemcopy((void*)(host->config.apbBase + MRAM_APB2TMC_DATA),
                    (void*)cfg->buffer, 16u);
        /* When the Ecc is on, the ECC information is also filled in.
        When the ECC is off, the data with a height of 18Bit is filled in */
        Mram_ApbWritel(host, MRAM_APB2TMC_DATA_146_128, cfg->info);
        Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
        apb2tmcCfg.data.apb2tmcTrig = 1;
        Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);

        if (MRAM_RET_OK != Mram_Apb2tmcWaitTrigDone(host)) {
            /* Wait trigger timeout */
            ret = MRAM_RET_TIMEOUT;
        }
        else {
            glbCtrl1.data.tmcCfgVblExtVmax = 0;
            if ((MRAM_BLOCK_TYPE_MAIN == cfg->type) ||
                (MRAM_BLOCK_TYPE_INFO_USER == cfg->type)) {
                apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_WRITE;
            }
            else if ((MRAM_BLOCK_TYPE_INFO_MARCO == cfg->type) ||
                    (MRAM_BLOCK_TYPE_OTP_USER == cfg->type) ||
                    (MRAM_BLOCK_TYPE_OTP_MARCO == cfg->type)){
                apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_WRITE_2CPB;
            }
            else if ((MRAM_BLOCK_TYPE_OTP_USER_2P2V == cfg->type) ||
                    (MRAM_BLOCK_TYPE_OTP_MARCO_2P2V == cfg->type)) {
                apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_WRITE_OTP;
                glbCtrl1.data.tmcCfgVblExtVmax = 1;
            }
            else {
                ret = MRAM_RET_UNSUPPORT;
            }

            if (MRAM_RET_OK == ret) {
                Mram_ApbWritel(host, MRAM_MRAMC_GLB_CTRL1, glbCtrl1.reg);
                apb2tmcCfg.data.apb2tmcTrig = 1;
                Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
                ret = Mram_Apb2tmcWaitTrigDone(host);
            }
        }

        /* Clear all touched register field to 0 is recommended. */
        Mram_ApbWritel(host, MRAM_MRAMC_GLB_CTRL1, tempGlbCtrl1);
        Mram_ApbWritel(host, MRAM_APB2TMC_CFG, 0x0u);

        (void) Mram_WriteStall(host, false);
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function Returns the value on the specified tmc register.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcReadRegByApb(
 *                          Mram_HostType *host, Mram_TmcRdWrRegCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : cfg - Register address and value for return
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Read successful
 *                      Others - Read failed
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcReadRegByApb(Mram_HostType *host, Mram_TmcRdWrRegCfgType *cfg)
{
    int32_t ret;
    Mram_Apb2tmcCfgType apb2tmcCfg;
    uint32_t tempReg = Mram_ApbReadl(host, MRAM_APB2TMC_CFG);

    /* The lower five digits of the address are always set to 1 */
    Mram_ApbWriteBits(host, MRAM_APB2TMC_CFG, MRAM_APB2TMC_CFG_ACC_ADDR_LSB, 20u,
        (cfg->addr << 5) | 0x1Fu);
    apb2tmcCfg.reg = Mram_ApbReadl(host, MRAM_APB2TMC_CFG);
    apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_READ_CONFIG;
    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
    apb2tmcCfg.data.apb2tmcTrig = 1;
    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);

    /* Wait for the transmission to complete until the timeout */
    ret = Mram_Apb2tmcWaitTrigDone(host);
    if (MRAM_RET_OK == ret) {
        cfg->regValue = Mram_ApbReadl(host, MRAM_TMC_REGIF_DOUT);
    }

    /* Clear all touched register field to 0 is recommended. */
    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, tempReg);

    return ret;
}

/********************************************************************************************************
 * \brief This function write values to the TMC register.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcWriteRegByApb(
 *                                  Mram_HostType *host, Mram_TmcRdWrRegCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      cfg - Register address and value parameters
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Write successful
 *                      Others - Write failed
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcWriteRegByApb(Mram_HostType *host, Mram_TmcRdWrRegCfgType *cfg)
{
    int32_t ret;
    Mram_Apb2tmcCfgType apb2tmcCfg;
    uint32_t tempReg = Mram_ApbReadl(host, MRAM_APB2TMC_CFG);

     /* The lower five digits of the address are always set to 1 */
    Mram_ApbWriteBits(host, MRAM_APB2TMC_CFG, MRAM_APB2TMC_CFG_ACC_ADDR_LSB, 20u,
        (cfg->addr << 5) | 0x1Fu);
    Mram_ApbWritel(host, MRAM_APB2TMC_DATA_31_0, cfg->regValue);
    apb2tmcCfg.reg = Mram_ApbReadl(host, MRAM_APB2TMC_CFG);
    apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_WRT_CONFIG;
    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
    apb2tmcCfg.data.apb2tmcTrig = 1;
    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);

    /* Wait for the transmission to complete until the timeout */
    ret = Mram_Apb2tmcWaitTrigDone(host);

    /* Clear all touched register field to 0 is recommended. */
    Mram_ApbWritel(host, MRAM_APB2TMC_CFG, tempReg);

    return ret;
}

/********************************************************************************************************
 * \brief This function Set the test_enable bit of the tmc register for write tmc registers.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcTestEnable(
 *                                  Mram_HostType *host, bool enable)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      enable - TMC register write enable switch
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : write other TMC register will be denid when test_enable = 0
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcTestEnable(Mram_HostType *host, bool enable)
{
    Mram_TmcRdWrRegCfgType cfg;
    cfg.addr = MRAM_TMC_R_TEST;
    cfg.regValue = (true == enable) ? 0x1u : 0x0u;
    return Mram_TmcWriteRegByApb(host, &cfg);
}

/********************************************************************************************************
 * \brief This function Select the trigger method for refcell rewrite.
 *
 * \verbatim
 * Syntax             : int32_t Mram_RefCellRewriteSetup(
 *                           Mram_HostType *host, Mram_RefCellRewriteModeType mode)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      mode - the trigger method for refcell rewrite
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Configuration successful
 *                      Others - Configuration failed
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_RefCellRewriteSetup(Mram_HostType *host, Mram_RefCellRewriteModeType mode)
{
    int32_t ret = MRAM_RET_OK;
    Mram_TmcRdWrRegCfgType cfg;
    cfg.addr = MRAM_TMC_R_REWRITE;
    cfg.regValue = 0u;

    if (MRAM_RET_OK != Mram_TmcReadRegByApb(host, &cfg)) {
        ret = MRAM_RET_FAILED;
    }
    else if ((cfg.regValue & MRAM_TMC_R_REWRITE_OVRD_VAL_MASK) == (uint32_t)mode) {
        /* This is currently the expected trigger mode, Do nothing */
    }
    /* You need to enable test_enable before writing to the register.*/
    else if (MRAM_RET_OK != Mram_TmcTestEnable(host, true)) {
        ret = MRAM_RET_FAILED;
    }
    else {
        /* Set the trigger mode of refcell-rewrite */
        cfg.regValue = (cfg.regValue & (~MRAM_TMC_R_REWRITE_OVRD_VAL_MASK)) | (uint32_t)mode;
        ret = Mram_TmcWriteRegByApb(host, &cfg);
        (void) Mram_TmcTestEnable(host, false);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function Set the key for write protection configuration.
 *
 * \verbatim
 * Syntax             : void Mram_PglkPasswordSetup(
 *                          Mram_HostType *host, uint64_t password)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      password - The value of write-protect key
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : The write protection can only be configured when the value of the write
 *                      protection key matches the value of the golden key.
 * \endverbatim
 *******************************************************************************************************/
static void Mram_PglkPasswordSetup(Mram_HostType *host, uint64_t password)
{
    /* The secret key has 64 bits and is divided into two register Settings */
    Mram_ApbWritel(host, MRAM_PGLK_PSWDL, (uint32_t)(password & 0xFFFFFFFFULL));
    Mram_ApbWritel(host, MRAM_PGLK_PSWDL + 4u, (uint32_t)((password >> 32) & 0xFFFFFFFFULL));
}

#if (MRAM_USE_APB2TMB)
/********************************************************************************************************
 * \brief This function read TMB(BIST engine) regiser by apb register.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmbRead(
 *                          Mram_HostType *host, uint32_t addr, uint64_t *value)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      addr - The address of the TMB
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : value - Return the value of the TMB register.
 *
 * Return value       : MRAM_RET_OK -Read successfully
 *                      Others - Read failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmbRead(Mram_HostType *host, uint32_t addr, uint64_t *value)
{
    int32_t ret = MRAM_RET_OK;
    Mram_Apb2tmbCtrlType apb2tmbCtrl;
    Mram_TmbRegData31To0Type data31To0;
    Mram_TmbRegData39To32Type data39To32;

    if (addr >= MRAM_TMB_REG_ADDR_MASK) {
        MRAM_LOG_ERROR("Address is invalid\r\n");
        ret = MRAM_RET_PARAMETER_ERR;
    }
    /* Determine whether the TMB is busy before accessing it */
    else if (MRAM_RET_OK != Mram_WaitForBitTimes(host->config.apbBase + MRAM_APB2TMB_CTRL,
        MRAM_APB2TMB_CTRL_BIST_BUSY_MASK, true, MRAM_MAX_POLLING_TMB_BIST_READY_TIMEOUT)) {
        MRAM_LOG_ERROR("Wait bist ready timeout\r\n");
        ret = MRAM_RET_TIMEOUT;
    }
    else {
        apb2tmbCtrl.reg = Mram_ApbReadl(host, MRAM_APB2TMB_CTRL);
        apb2tmbCtrl.data.bistEn = 1u;
        apb2tmbCtrl.data.tmbRegAccEn = 1u;
        apb2tmbCtrl.data.tmbRegAccType = 0u;
        apb2tmbCtrl.data.tmbRegAddr = addr;
        apb2tmbCtrl.data.tmbRegAccTrig = 1u;
        Mram_ApbWritel(host, MRAM_APB2TMB_CTRL, apb2tmbCtrl.reg);

        if (MRAM_RET_OK != Mram_WaitForBitTimes(host + MRAM_APB2TMB_CTRL,
        MRAM_APB2TMB_CTRL_TMB_REG_ACC_TRIG_MASK, true, MRAM_MAX_POLLING_TMB_TRIGE_TIMEOUT)) {
            MRAM_LOG_ERROR("Wait triger done timeout\r\n");
            ret = MRAM_RET_TIMEOUT;
            apb2tmbCtrl.data.bistEn = 0u;
            apb2tmbCtrl.data.tmbRegAccTrig = 0u;
            Mram_ApbWritel(host, MRAM_APB2TMB_CTRL, apb2tmbCtrl.reg);
        }
        else {
            data31To0.reg = Mram_ApbReadl(host, MRAM_TMB_REG_DATA_31_0);
            data39To32.reg = Mram_ApbReadl(host, MRAM_TMB_REG_DATA_39_32);
            *value = data31To0.data.data31To0 | ((uint64_t)data39To32.data.data39To32 << 32);
        }
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function write TMB(BIST engine) regiser by apb register.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmbWrite(
 *                          Mram_HostType *host, uint32_t addr, uint64_t value)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      addr - The address of the TMB
 *                      value - The value to be written
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK -Write successfully
 *                      Others - Write failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmbWrite(Mram_HostType *host, uint32_t addr, uint64_t value)
{
    int32_t ret = MRAM_RET_OK;
    Mram_Apb2tmbCtrlType apb2tmbCtrl;
    Mram_TmbRegData31To0Type data31To0;
    Mram_TmbRegData39To32Type data39To32;

    if (addr >= MRAM_TMB_REG_ADDR_MASK) {
        MRAM_LOG_ERROR("Address is invalid\r\n");
        ret = MRAM_RET_PARAMETER_ERR;
    }
    else if (MRAM_RET_OK != Mram_WaitForBitTimes(host->config.apbBase + MRAM_APB2TMB_CTRL,
        MRAM_APB2TMB_CTRL_BIST_BUSY_MASK, true, MRAM_MAX_POLLING_TMB_BIST_READY_TIMEOUT)) {
        MRAM_LOG_ERROR("Wait bist ready timeout\r\n");
        ret = MRAM_RET_TIMEOUT;
    }
    else {
        data31To0.data.data31To0 = value;
        data39To32.data.data39To32 = value >> 32;
        Mram_ApbWritel(host, MRAM_TMB_REG_DATA_31_0, data31To0.reg);
        Mram_ApbWritel(host, MRAM_TMB_REG_DATA_39_32, data39To32.reg);

        apb2tmbCtrl.reg = Mram_ApbReadl(host, MRAM_APB2TMB_CTRL);
        apb2tmbCtrl.data.bistEn = 1u;
        apb2tmbCtrl.data.tmbRegAccEn = 1u;
        apb2tmbCtrl.data.tmbRegAccType = 1u;
        apb2tmbCtrl.data.tmbRegAddr = addr;
        apb2tmbCtrl.data.tmbRegAccTrig = 1u;
        Mram_ApbWritel(host, MRAM_APB2TMB_CTRL, apb2tmbCtrl.reg);

        if (MRAM_RET_OK != Mram_WaitForBitTimes(host->config.apbBase + MRAM_APB2TMB_CTRL,
        MRAM_APB2TMB_CTRL_TMB_REG_ACC_TRIG_MASK, true, MRAM_MAX_POLLING_TMB_TRIGE_TIMEOUT)) {
            MRAM_LOG_ERROR("Wait triger done timeout\r\n");
            ret = MRAM_RET_TIMEOUT;
            apb2tmbCtrl.data.bistEn = 0u;
            apb2tmbCtrl.data.tmbRegAccTrig = 0u;
            Mram_ApbWritel(host, MRAM_APB2TMB_CTRL, apb2tmbCtrl.reg);
        }
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function Wait for the BIST transfer to complete.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmbWaitBistDone(
 *                              Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - The state of waiting for the transfer to BIST to complete
 *                      MRAM_RET_FAILED - Unable to access BIST
 *                      MRAM_RET_TIMEOUT - Timeout transfer did not complete
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmbWaitBistDone(Mram_HostType *host)
{
    int32_t ret = MRAM_RET_OK;
    uint64_t status;
    uint32_t tryCnt = MRAM_MAX_POLLING_BIST_DONE_TIMEOUT;


    while ((tryCnt--) && (MRAM_RET_OK == ret)) {
        if (MRAM_RET_OK != Mram_TmbRead(host, 0x9u, &status)) {
            ret = MRAM_RET_FAILED;
        }
        else if (status & 0x1) {
            /* Continue */
        }
        else {
            break;
        }
    }

    if (0 == tryCnt) {
        ret = MRAM_RET_TIMEOUT;
    }

    return ret;
}

static int32_t Mram_TmcReadByBist(Mram_HostType *host, Mram_TmcRdWrWordCfgType *cfg)
{
    return MRAM_RET_UNSUPPORT;
}

static int32_t Mram_TmcWriteByBist(Mram_HostType *host, Mram_TmcRdWrWordCfgType *cfg)
{
    return MRAM_RET_UNSUPPORT;
}

/********************************************************************************************************
 * \brief This function Read the TMC register through the BIST interface.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcReadRegByBist(
 *                          Mram_HostType *host, Mram_TmcRdWrRegCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : cfg - tmc register address and return value buffer
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Read successful
 *                      Others - Read failed
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcReadRegByBist(Mram_HostType *host, Mram_TmcRdWrRegCfgType *cfg)
{
    int32_t ret = MRAM_RET_FAILED;
    uint64_t dataTemp;
    if (MRAM_RET_OK != Mram_TmbWrite(host, 0x3u, 0x6000u)) {
        MRAM_LOG_ERROR("Write bist reg 0x3u failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbWrite(host, 0x4u, (cfg->addr << 5) | 0x1Fu)) {
        MRAM_LOG_ERROR("Write bist reg 0x4u failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbWrite(host, 0x9u, 0x110u)) {
        MRAM_LOG_ERROR("Write bist reg 0x9u failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbWaitBistDone(host)) {
        MRAM_LOG_ERROR("Wait bist done failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbRead(host, 0xeu, &dataTemp)) {
        MRAM_LOG_ERROR("Read bist reg 0xeu failed\r\n");
    }
    else {
        cfg->regValue = dataTemp & 0xFFFFFFFFu;
        ret = MRAM_RET_OK;
    }

    /* Empty the tmb register after reading */
    (void) Mram_TmbWrite(host, 0x3u, 0u);
    (void) Mram_TmbWrite(host, 0x4u, 0u);
    (void) Mram_TmbWrite(host, 0x9u, 0u);
    return ret;
}

/********************************************************************************************************
 * \brief This function Write tmc registers through the BIST interface.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcWriteRegByBist(
 *                          Mram_HostType *host, Mram_TmcRdWrRegCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      cfg - The address and value of the TMC register written
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Write successful
 *                      Others - Write fail
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcWriteRegByBist(Mram_HostType *host, Mram_TmcRdWrRegCfgType *cfg)
{
    int32_t ret = MRAM_RET_FAILED;

    if (MRAM_RET_OK != Mram_TmbWrite(host, 0x3u, 0x6000u)) {
        MRAM_LOG_ERROR("Write bist reg 0x3u failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbWrite(host, 0x4u, (cfg->addr << 5) | 0x1Fu)) {
        MRAM_LOG_ERROR("Write bist reg 0x4u failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbWrite(host, 0x7u, 0u)) {
        MRAM_LOG_ERROR("Write bist reg 0x7u failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbWrite(host, 0x5u, (uint64_t)cfg->regValue)) {
        MRAM_LOG_ERROR("Write bist reg 0x5u failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbWrite(host, 0x9u, 0x10u)) {
        MRAM_LOG_ERROR("Write bist reg 0x9u failed\r\n");
    }
    else if (MRAM_RET_OK != Mram_TmbWaitBistDone(host)) {
        MRAM_LOG_ERROR("Wait bist done failed\r\n");
    }
    else {
        ret = MRAM_RET_OK;
    }

    /* Empty the register after writing */
    (void) Mram_TmbWrite(host, 0x3u, 0u);
    (void) Mram_TmbWrite(host, 0x4u, 0u);
    (void) Mram_TmbWrite(host, 0x5u, 0u);
    (void) Mram_TmbWrite(host, 0x7u, 0u);
    (void) Mram_TmbWrite(host, 0x9u, 0u);

    return ret;
}

#endif /* #if (MRAM_USE_APB2TMB) */

/********************************************************************************************************
 * \brief This function Read the registers of the mram to obtain information about the mram control.
 *
 * \verbatim
 * Syntax             : void Mram_InfoSetup(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Controller information such as version number and size.
 * \endverbatim
 *******************************************************************************************************/
static void Mram_InfoSetup(Mram_HostType *host)
{
    Mram_RevisionType revision;
    Mram_MramSizeType mramSize;

    revision.reg = Mram_ApbReadl(host, MRAM_REVISION);
    mramSize.reg = Mram_ApbReadl(host, MRAM_MRAM_SIZE);

    host->info.majorRevision = revision.data.majorRevision;
    host->info.minorRevision = revision.data.minorRevision;
    host->info.phaseRevision = revision.data.phaseRevision;
    host->info.mramVersion = mramSize.data.mramVersion;
    host->info.totalSize = (MRAM_TYPE_32M == host->info.mramVersion) ?
                    MRAM_TYPE_32M_SIZE_BYTE : MRAM_TYPE_4M_SIZE_BYTE;

    MRAM_LOG_DEBUG("Mram info: version(%d-%d-%d-%d), Size(0x%x)\r\n",
         host->info.majorRevision, host->info.minorRevision,
         host->info.phaseRevision, host->info.mramVersion,
         host->info.totalSize);
}

/********************************************************************************************************
 * \brief This function returns the global password for write protected.
 *
 * \verbatim
 * Syntax             : uint64_t Mram_GetPglkGlobalPassword(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Returns the 64Bit password
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static uint64_t Mram_GetPglkGlobalPassword(Mram_HostType *host)
{
#ifdef CFG_PLATFORM_MCAL
    return readll(host->config.apbBase + MRAM_PGLK_GPSWDL);
#else
    return readq(host->config.apbBase + MRAM_PGLK_GPSWDL);
#endif
}

/********************************************************************************************************
 * \brief This function returns the version information of this module.
 *
 * \verbatim
 * Syntax             : void Mram_PglkGlobalPasswordSetup(
 *                              Mram_HostType *host, uint64_t pglkPassword)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      pglkPassword - The static password is configured
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : The initial configuration is performed once and you do not need to
 *                      modify it later
 * \endverbatim
 *******************************************************************************************************/
static void Mram_PglkGlobalPasswordSetup(Mram_HostType *host, uint64_t pglkPassword)
{
    Mram_ApbWritel(host, MRAM_PGLK_GPSWDL, (uint32_t)(pglkPassword & 0xFFFFFFFFULL));
    Mram_ApbWritel(host, MRAM_PGLK_GPSWDL + 4u, (uint32_t)((pglkPassword >> 32) & 0xFFFFFFFFULL));
    host->pglkPassword = pglkPassword;
}

/********************************************************************************************************
 * \brief This function returns the switch status of the write buffer.
 *
 * \verbatim
 * Syntax             : bool Mram_WrBufferGetSwitchStatus(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : TRUE - The wrbuffer is enabled
 *                      FALSE - The wrbuffer is disable
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static bool Mram_WrBufferGetSwitchStatus(Mram_HostType *host)
{
    Mram_Axi2tmcWrCfgType wrCfg;
    wrCfg.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_WR_CFG);
    return (wrCfg.data.wrbufDis == true) ? false : true;
}

/********************************************************************************************************
 * \brief This function switch the status of the wrbuffer.
 *
 * \verbatim
 * Syntax             : void Mram_WrBufferSetup(
 *                          Mram_HostType *host, bool wrBufferEnable)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      wrBufferEnable - wrbuffer expect status to be configured
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static void Mram_WrBufferSetup(Mram_HostType *host, bool wrBufferEnable)
{
    Mram_Axi2tmcWrCfgType wrCfg;

    wrCfg.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_WR_CFG);
    /* Polarity reversal of switch and register values */
    wrCfg.data.wrbufDis = (true == wrBufferEnable) ? 0u : 1u;
    Mram_ApbWritel(host, MRAM_AXI2TMC_WR_CFG, wrCfg.reg);

    /* Disable interrupt signal for write buffer flush done */
    Mram_ApbWriteBits(host, MRAM_MRAMC_FUNC_INT0_STA_EN,
        MRAM_MRAMC_FUNC_INT0_STA_EN_FLUSH_DONE_LSB, 1u, 1u);
    Mram_ApbWriteBits(host, MRAM_MRAMC_FUNC_INT0_SIG_EN,
        MRAM_MRAMC_FUNC_INT0_SIG_EN_FLUSH_DONE_LSB, 1u, 0u);
    host->wrBufferEnable = wrBufferEnable;
}

/********************************************************************************************************
 * \brief This function Record the address of a repairable ECC error.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostAddEccErrAddress(
 *                                  Mram_HostType *host, uint32_t mramErrAddr)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      mramErrAddr - Error address
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Record success
 *                      Others - Record failure
 *
 * Description        : This interface has the duplicate check function. If the maximum number is
 *                      reached, a failure is returned
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_HostAddEccErrAddress(Mram_HostType *host, uint32_t mramErrAddr)
{
    int32_t ret = MRAM_RET_OK;
    uint8_t i, firstInvalidIndex = MRAM_ECC_ERROR_ADDRESS_MAX;
    Mram_WordAddressType errAddr;
    errAddr.data = 0u;

    if (host->eccErrAddrNum >= MRAM_ECC_ERROR_ADDRESS_MAX) {
        ret = MRAM_RET_BOUND_ERR;
    }
    else {
        errAddr.address.wordAddress = mramErrAddr;
        /* Skip duplicate error addresses */
        for (i = 0; i < MRAM_ECC_ERROR_ADDRESS_MAX; i++) {
            if ((host->eccErrAddrTable[i].mramWordAddress.data == errAddr.data) &&
                (true == host->eccErrAddrTable[i].invalidFlag)) {
                /* Duplicate address, ignore directly */
                MRAM_LOG_DEBUG("address(0x%x) has been added\r\n", errAddr.address.wordAddress);
                firstInvalidIndex = MRAM_ECC_ERROR_ADDRESS_MAX;
                break;
            }
            else if ((MRAM_ECC_ERROR_ADDRESS_MAX == firstInvalidIndex) &&
                    (false == host->eccErrAddrTable[i].invalidFlag)) {
                /* Record the location of the first error address that can be filled */
                firstInvalidIndex = i;
            }
            else {
                /* Do nothing */
            }
        }
        if (MRAM_ECC_ERROR_ADDRESS_MAX != firstInvalidIndex) {
            /* Add new error address */
            host->eccErrAddrTable[firstInvalidIndex].invalidFlag = true;
            host->eccErrAddrTable[firstInvalidIndex].mramWordAddress.data = errAddr.data;
            host->eccErrAddrNum++;
            MRAM_LOG_DEBUG("Add address(0x%x)\r\n", errAddr.address.wordAddress);
        }
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function returns an error code and abort processing.
 *
 * \verbatim
 * Syntax             : uint32_t Mram_IrqHandler(
 *                              Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Event code
 *
 * Description        : Only valid interrupt events are processed
 * \endverbatim
 *******************************************************************************************************/
static uint32_t Mram_IrqHandler(Mram_HostType *host)
{
    Mram_MramcFuncInt0Type int0St;
    Mram_MramcFuncInt0Type int0StVld;
    Mram_MramcFuncInt1Type int1St;
    Mram_MramcFuncInt1Type int1StVld;
    uint32_t event = 0;

    int0St.reg = Mram_ApbReadl(host, MRAM_MRAMC_FUNC_INT0_STA);
    int1St.reg = Mram_ApbReadl(host, MRAM_MRAMC_FUNC_INT1_STA);
    int0StVld.reg = Mram_ApbReadl(host, MRAM_MRAMC_FUNC_INT0_SIG_EN);
    int1StVld.reg = Mram_ApbReadl(host, MRAM_MRAMC_FUNC_INT1_SIG_EN);

    MRAM_LOG_DEBUG("INT0:0x%x 0x%x\r\n", int0St.reg, int0StVld.reg);
    MRAM_LOG_DEBUG("INT1:0x%x 0x%x\r\n", int1St.reg, int1StVld.reg);

    if (int0St.data.mramErrAddVld != 0u) {
        if (int0St.data.tmcReadErr != 0u) {
            /* Unrepairable ECC error. ignore */
            event |= MRAM_EVENT_MEMORY_DAMAGED;
        }
        else {
            /* Fixable errors need to be documented */
            (void) Mram_HostAddEccErrAddress(host, Mram_ApbReadl(host, MRAM_ERR_ADDR));
            if (host->eccErrAddrNum >= MRAM_ECC_ERROR_ADDRESS_MAX) {
                event |= MRAM_EVENT_ECC_NEED_REPAIR;
                /* Turn off the interrupt for mram cor error */
                Mram_ApbWriteBits(host, MRAM_MRAMC_FUNC_INT0_SIG_EN,
                    MRAM_MRAMC_FUNC_INT0_SIG_EN_MRAM_ERR_ADDR_VLD_LSB, 1u, 0u);
            }
        }
    }

    /* Hardware error handling, software rewakeup */
    if (int0St.data.tmcRecallKeyErr != 0u) {
        if (MRAM_RET_OK != Mram_AutoWakeup(host)) {
            event |= MRAM_EVENT_MEMORY_DAMAGED;
        }
    }

    if (int0St.data.pglockAreaDeny != 0u) {
        /* The write protected area is accessed */
        event |= MRAM_EVENT_PGLOCK_AREA_DENY;
    }

    if (int1St.data.crcChkFail != 0u) {
        /* loop mode crc monitoring result failed. Procedure */
        event |= MRAM_EVENT_CRC_CHECK_ERROR;
    }

    /* Clears only the interrupt state that triggers the enable */
    Mram_ApbWritel(host, MRAM_MRAMC_FUNC_INT0_STA, int0St.reg & int0StVld.reg);
    Mram_ApbWritel(host, MRAM_MRAMC_FUNC_INT1_STA, int1St.reg & int1StVld.reg);

    return event;
}

/********************************************************************************************************
 * \brief This function Initialize the mram controller.
 *
 * \verbatim
 * Syntax             : void Mram_HostIpInit(
 *                          Mram_HostType *host, const Mram_HostConfigType *config)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      config - Configuration parameter
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static void Mram_HostIpInit(Mram_HostType *host, const Mram_HostConfigType *config)
{
    MRAM_LOG_DEBUG("Mram_HostIpInit %d\r\n", config->aclkHz);
    (void) Mram_HostMemcopy((void*)&host->config, (const void*)&config->config,
                                sizeof(Mram_HostStaticConfigType));
    host->aclkHz = config->aclkHz;
    host->wrBufferEnable = false;
    host->useInterrupt = false;
    host->tmcClkHz = Mram_TmcClkGet(host);
    if (MRAM_INIT_BYPASS == host->config.initialMode) {
        /* This bypass mode controller state is obtained from a register */
        host->wrBufferEnable = Mram_WrBufferGetSwitchStatus(host);
        host->pglkPassword = Mram_GetPglkGlobalPassword(host);
    }
    else {
        Mram_WrBufferSetup(host, config->wrBufferEnable);
        Mram_WrIntervalSetup(host, config->wrIntervalUs);
        Mram_PglkGlobalPasswordSetup(host, MRAM_PGLK_PASSWORD);
    }
    /* Fixed enabling the wake up function of runtiming */
    Mram_RuntimeWakeupSetup(host, true);
    Mram_InfoSetup(host);

    host->dmaMaxSize = 512u;
}

#if CONFIG_MRAM_SUPPORT_UNTRIMMED_CHIP
/********************************************************************************************************
 * \brief This function send the wake up command to the tmc.
 *
 * \verbatim
 * Syntax             : int32_t Mram_Wakeup(
 *                              Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Sending command succeeded
 *                      Others - Failed to send command
 *
 * Description        : This function is only used on chips without trim
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_Wakeup(Mram_HostType *host)
{
    int32_t ret = MRAM_RET_FAILED;
    Mram_Apb2tmcCfgType apb2tmcCfg;

    apb2tmcCfg.reg = 0u;

    if(MRAM_RET_OK == Mram_WaitForBitTimes(  host->config.apbBase + MRAM_MRAMC_STA,
        MRAM_MRAMC_STA_TMC_SLP_STA_MASK, true, MRAM_MAX_POLLING_SLP_STA_TIMEOUT)) {
        apb2tmcCfg.data.apb2tmcCmd = (Mram_BitFieldType)MRAM_TMC_CMD_WAKEUP;
        Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
        apb2tmcCfg.data.apb2tmcTrig = 1;
        Mram_ApbWritel(host, MRAM_APB2TMC_CFG, apb2tmcCfg.reg);
        /* Wait for mramc read some important trim information from MRAM, or rewrite complete */
        ret = Mram_Apb2tmcWaitTrigDone(host);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function write to the tmc register.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TmcRegWrite(
 *                          Mram_HostType *host, uint32_t addr, uint32_t value)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      addr - The address of tmc register
 *                      value - Written data
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Write successfully
 *                      Others - Write failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TmcRegWrite(Mram_HostType *host, uint32_t addr, uint32_t value)
{
    Mram_TmcRdWrRegCfgType rdWrRegCfg;

    rdWrRegCfg.addr = addr;
    rdWrRegCfg.regValue = value;
#if MRAM_USE_APB2TMB
    return Mram_TmcWriteRegByBist(host, &rdWrRegCfg);
#else
    return Mram_TmcWriteRegByApb(host, &rdWrRegCfg);
#endif
}

/********************************************************************************************************
 * \brief This function Use the trim information to initialize tmc.
 *
 * \verbatim
 * Syntax             : int32_t Mram_TsmcInit(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Successful initialization
 *                      Others - Initialization failure
 *
 * Description        : MRAM must be initialized with trim information before normal read / write,
 *                      otherwise the read / write may be failed.
 * \endverbatim
 *******************************************************************************************************/
static int32_t Mram_TsmcInit(Mram_HostType *host)
{
    int32_t ret = MRAM_RET_FAILED;

    if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x0u, 0x1u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0xcu, 0x742108u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0xdu, 0x2a1e2au)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0xfu, 0xb1u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x10u, 0x400u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x11u,
                (MRAM_TYPE_32M == host->info.mramVersion) ? 0xc008993u : 0xc008773u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x1u, 0x3d8078c9u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0xbu, 0x18000000u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x15u, 0x1cu)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x6u, 0x100012cu)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x7u, 0xc00054u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x2u, 0x0u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x3u, 0x0u)) {
        /* Do nothing */
    } else if (MRAM_RET_OK != Mram_TmcRegWrite(host, 0x17u, 0x1520520u)) {
        /* Do nothing */
    } else {
        ret = MRAM_RET_OK;
    }

    return ret;
}
#endif /* #if CONFIG_MRAM_SUPPORT_UNTRIMMED_CHIP */

/***************************************************************************************************
 *                                  Global Function Declarations                                   *
 **************************************************************************************************/
/********************************************************************************************************
 * \brief This function Initialize the mram controller and tmc.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostInit(
 *                          Mram_HostType *host, const Mram_HostConfigType *config)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      config - Initialization parameter
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Successful initialization
 *                      Others - Initialization failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostInit(Mram_HostType *host, const Mram_HostConfigType *config)
{
    int32_t ret = MRAM_RET_FAILED;

    /* Controller initialization */
    Mram_HostIpInit(host, config);
    if (MRAM_INIT_BYPASS == host->config.initialMode) {
        /* The automatic wakeup process has already been done on other cores, so we'll skip it here. */
        ret = MRAM_RET_OK;
    }
    else if (MRAM_RET_OK != Mram_WaitReady(host)) {
        /* Do nothing */
    }
#if CONFIG_MRAM_SUPPORT_UNTRIMMED_CHIP
    else if (MRAM_INIT_UNTRIMMED_SW_WAKEUP == host->config.initialMode) {
        /* Mram untrimmed(blinded chip), sw initial sequence */
        if (MRAM_RET_OK != Mram_HostTmcClkSetup(host, config->tmcClkHz)) {
            MRAM_LOG_ERROR("Clock setup failed\r\n");
        } else if (MRAM_RET_OK != Mram_Wakeup(host)) {
            MRAM_LOG_ERROR("Mram wakeup failed\r\n");
        } else if (MRAM_RET_OK != Mram_TsmcInit(host)) {
            MRAM_LOG_ERROR("Mram init TSMC failed\r\n");
        } else {
            ret = MRAM_RET_OK;
        }
    }
#endif /* #if CONFIG_MRAM_SUPPORT_UNTRIMMED_CHIP */
    else if (MRAM_INIT_RST_AUTO_WAKEUP == host->config.initialMode) {
        /* Mram trimmed, hw initial sequence */
        ret = MRAM_RET_OK;
    }
    else {
        /* Mram trimmed, sw initial sequence */
        if (MRAM_RET_OK != Mram_HostTmcClkSetup(host, config->tmcClkHz)) {
            MRAM_LOG_ERROR("Clock setup failed\r\n");
        }
        /* Software autowakeup */
        else if (MRAM_RET_OK != Mram_AutoWakeup(host)) {
            MRAM_LOG_ERROR("Mram wakeup failed\r\n");
        }
        /* During the sequence, MRAM Controller will automatically read some important trim
        information from MRAM.*/
        else if (MRAM_RET_OK != Mram_RrlutSwLoad(host)) {
            MRAM_LOG_ERROR("Mram rrlut sw load failed\r\n");
        }
        else {
            ret = MRAM_RET_OK;
        }
    }

    if (MRAM_RET_OK == ret) {
        host->status = MRAM_READY;
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function Configure frequency division coefficient and timing.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostTmcClkSetup(
 *                          Mram_HostType *host, uint32_t tmcClk)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      tmcClk - Expected tmc frequency
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Configuration successful
 *                      Others - Configuration failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostTmcClkSetup(Mram_HostType *host, uint32_t tmcClk)
{
    int32_t ret = MRAM_RET_FAILED;
    uint8_t divNum = (uint8_t)(MRAM_DIV_ROUND_UP(host->aclkHz, tmcClk));

    /* The configuration always needs to meet the requirements */
    if ((tmcClk < MRAM_MIN_TMC_CLK_HZ) || (tmcClk > MRAM_MAX_TMC_CLK_HZ)) {
        ret = MRAM_RET_UNSUPPORT;
        MRAM_LOG_ERROR("TmcClk rate invalid (%d %d)(%d)\r\n", host->aclkHz, tmcClk, divNum);
    }
    /* The maximum value of the frequency division is 0xF */
    else if ((divNum == 0u) || (divNum > 0x10u)) {
        ret = MRAM_RET_PARAMETER_ERR;
        MRAM_LOG_ERROR("Clk rate invalid (%d %d)(%d)\r\n", host->aclkHz, tmcClk, divNum);
    }
    /* If wr stall is enabled, subsequent writes cannot be triggered temporarily; otherwise,
    they will die */
    else if (MRAM_RET_OK != Mram_WriteStall(host, true)) {
        MRAM_LOG_ERROR("Write stall failed\r\n");
    }
    else {
        if (host->tmcClkHz > tmcClk) {
            /* TMC clock frqguency down*/
            host->tmcClkHz = host->aclkHz / divNum;
            if (MRAM_RET_OK != Mram_TmcClkDivSetup(host, divNum - 1u)) {
                /* Do nothing */
            } else if(MRAM_RET_OK != Mram_ExtTmcRtimerSetup(host)) {
                /* Do nothing */
            } else {
                ret = MRAM_RET_OK;
            }
        } else {
            /* TMC clock frqguency up*/
            host->tmcClkHz = host->aclkHz / divNum;
            if (MRAM_RET_OK != Mram_ExtTmcRtimerSetup(host)) {
                /* Do nothing */
            } else if (MRAM_RET_OK != Mram_TmcClkDivSetup(host, divNum - 1u)) {
                /* Do nothing */
            } else {
                ret = MRAM_RET_OK;
            }
        }

        /* The frequency may change. You need to update the frequency-related configuration */
        Mram_WrIntervalSetup(host, host->wrIntervalUs);

        (void) Mram_WriteStall(host, false);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function Is to interrupt the processing interface and invoke external callbacks.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostIrqHandler(
 *                           uint32_t irq, void *arg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Fixed format of interrupt, return meaningless
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostIrqHandler(uint32_t irq, void *arg)
{
    Mram_HostType *host = (Mram_HostType *)arg;
    uint32_t event;

    irq_log_check_valid(irq);

    /* Invoke the underlying interrupt handling, Invokes the underlying interrupt handling and
    returns the event value*/
    event = Mram_IrqHandler(host);

    if ((NULL != host->eventListener) && (0u != event)) {
        /* An external callback is called when there is a callback and an event occurs */
        host->eventListener(host->eventListenerArg, event);
    }
    return MRAM_RET_OK;
}

/********************************************************************************************************
 * \brief This function fixes ECC errors that have been recorded.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostEccRepair(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - All repaired successfully
 *                      Others - An unexpected error occurred
 *
 * Description        : After the repair is completed successfully, if the Ecc error is interrupted,
 *                      the shutdown is turned on, so that the ECC error can continue to be recorded
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostEccRepair(Mram_HostType *host)
{
    int32_t ret = MRAM_RET_OK;
    uint8_t j;
    uint8_t localBuffer[MRAM_WORD_SIZE_BYTE];
    Mram_WordAddressType address_temp;

    if (host->eccErrAddrNum > 0u) {
        for (j = 0; (j < MRAM_ECC_ERROR_ADDRESS_MAX) && (MRAM_RET_OK == ret); j++) {
            if (true == host->eccErrAddrTable[j].invalidFlag) {
                /* The address recorded for Ecc error repair is the mram word address, which needs
                to be converted to the address on the AXI bus */
                address_temp.address.wordAddress = Mram_HostEccErrAddrConvert(
                            host, host->eccErrAddrTable[j].mramWordAddress);
                /* Workaround: Ecc err report address Need to do address translation twice */
                address_temp.address.wordAddress = Mram_HostEccErrAddrConvert(host, address_temp);
                if (MRAM_RET_OK != Mram_HostRead(host, address_temp.address.wordAddress << 4,
                                    localBuffer, MRAM_WORD_SIZE_BYTE)) {
                    ret = MRAM_RET_FAILED;
                }
                else {
                    /* Write back the correct data once to fix it */
                    (void) Mram_HostWrite(host, address_temp.address.wordAddress << 4,
                                    localBuffer, MRAM_WORD_SIZE_BYTE);
                    host->eccErrAddrTable[j].invalidFlag = false;
                    host->eccErrAddrNum--;
                }
            }
        }
    }

    if (MRAM_RET_OK == ret) {
        /* Turn on the interrupt for mram cor error */
        Mram_ApbWriteBits(host, MRAM_MRAMC_FUNC_INT0_SIG_EN,
            MRAM_MRAMC_FUNC_INT0_SIG_EN_MRAM_ERR_ADDR_VLD_LSB, 1u, 1u);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function make a data copy.
 *
 * \verbatim
 * Syntax             : void* Mram_HostMemcopy(
 *                          void *dst, const void *src, uint32_t totalSize)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : dst - Destination address
 *                      src - Source address
 *                      totalSize - Data length
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Return to destination address
 *
 * Description        : This interface does not require address alignment, but alignment of
 *                      transmissions is recommended for better performance. The CPU cache
 *                      needs to be maintained on the upper layer
 * \endverbatim
 *******************************************************************************************************/
void* Mram_HostMemcopy(void *dst, const void *src, uint32_t totalSize)
{
    uint32_t size = totalSize;
    uint32_t alignSrc = (uint32_t)src % 4UL;
    uint32_t alignDst = (uint32_t)dst % 4UL;
    uint8_t *dstPtr = (uint8_t *)dst;
    const uint8_t *srcPtr = (const uint8_t *)src;
    uint32_t i;
    uint32_t bytes;

    if (alignSrc != alignDst) {
        /* Unaligned copy of data */
        for (; size > 0u; size--) {
            *dstPtr = *srcPtr;
            ++dstPtr;
            ++srcPtr;
        }
    } else {
        if (0u != alignSrc) {
            /* First copy the misaligned parts and crop them to align */
            bytes = (size > (4u - alignDst)) ? (4u - alignDst) : size;

            for (i = 0u; i < bytes ; i++) {
                *dstPtr = *srcPtr;
                --size;
                ++dstPtr;
                ++srcPtr;
            }
        }

        if (size > 4u) {
            /* First copy the misaligned parts and crop them to align */
            bytes = size / 4u * 4u;
            (void) Mram_4BytesAlignedMemcpy((void*)dstPtr, (const void*)srcPtr, bytes);
            dstPtr += bytes;
            srcPtr += bytes;
            size -= bytes;
        }

        for (; size > 0u; size--) {
            /* Copy the last part of the length that does not align */
            *dstPtr = *srcPtr;
            ++dstPtr;
            ++srcPtr;
        }
    }
    return dst;
}

/********************************************************************************************************
 * \brief This function Configure the properties and switches of mram's readbuffer channel.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostRdBufferSetup(
 *                          Mram_HostType *host, const Mram_RdBufferCfgType* rdbufferCfg, uint8_t num)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      rdbufferCfg - Configuration channel list
 *                      num - Number of configured channels
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Configuration successful
 *                      Others - Configuration failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostRdBufferSetup(Mram_HostType *host, const Mram_RdBufferCfgType* rdbufferCfg, uint8_t num)
{
    uint8_t i;
    int32_t ret = MRAM_RET_OK;
    Mram_Axi2tmcRdCfgType rdCfg;

    for (i = 0; i < num; i++) {
        if (MRAM_RET_OK != Mram_RdbufferChannelConfig(host, &rdbufferCfg[i])) {
            /* Invalid configuration */
            ret = MRAM_RET_FAILED;
            break;
        }
    }

    if (MRAM_RET_OK == ret) {
        rdCfg.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_RD_CFG);
        /* Use quick round robin mode by default */
        rdCfg.data.rdbufRoundMode = 0;
        /* Feedback for AXI error is disabled by default */
        rdCfg.data.rdRespEn = 0;
        Mram_ApbWritel(host, MRAM_AXI2TMC_RD_CFG, rdCfg.reg);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function returns the version information of this module.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostGetValidImageRgnIndex(
 *                          Mram_HostType *host, uint8_t *index)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : index - Returns the available image region index
 *
 * Return value       : MRAM_RET_OK - There are areas available
 *                      MRAM_RET_BOUND_ERR - No area is available
 *
 * Description        : The effective meaning of an interface is that it can be used. If the hardware
 *                      Settings of a region are invalid, the external configuration can be reconfigured.
 *                      Otherwise, the existing configuration region will be damaged.
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostGetValidImageRgnIndex(Mram_HostType *host, uint8_t *index)
{
    int32_t ret = MRAM_RET_BOUND_ERR;
    Mram_ImgCrcRgnVldType rgnVld;
    uint8_t i;

    /* Iterate through all areas to see if they are already in use */
    for (i = 0; i < MRAM_IMG_CRC_REGION_NUM; i++) {
        rgnVld.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_VLD + (0x40u * (uint32_t)i));
        if (rgnVld.data.valid == 0u) {
            /* An invalid image region was found */
            *index = i;
            ret = MRAM_RET_OK;
            break;
        }
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function Configure the region and crc information of the image, and the region validity.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostImageCrcSetup(
 *                          Mram_HostType *host, const Mram_ImageCrcCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      cfg - Configuration parameter
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Configuration successful
 *                      Others - Configuration failure
 *
 * Description        : For the area being checked, the check stops immediately after it is
 *                      set to invalid
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostImageCrcSetup(Mram_HostType *host, const Mram_ImageCrcCfgType *cfg)
{
    Mram_ImgCrcRgnAddrType rgnAddr;
    Mram_ImgCrcRgnSizeType rgnSize;
    Mram_ImgCrcRgnVldType rgnVld;
    int32_t cfgRet;
    rgnAddr.reg = 0u;
    rgnSize.reg = 0u;

    if (cfg->index >= MRAM_IMG_CRC_REGION_NUM) {
        cfgRet = MRAM_RET_BOUND_ERR;
    }
    else {
        rgnVld.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_VLD + (0x40u * (uint32_t)cfg->index));
        if (rgnVld.data.valid == cfg->valid) {
            /* The current invalid area does not need to be closed again, directly return success.
            The current area is valid and cannot be configured again */
            cfgRet = (0u != cfg->valid) ? MRAM_RET_FAILED : MRAM_RET_OK;
            MRAM_LOG_ERROR("Mram{0x%x} rgn-%d has been (%d)\r\n",
                host->config.apbBase, cfg->index, cfg->valid);
        }
        else {
            /* Close the last check and clear the results */
            Mram_CrcCheckStopTrigger(host, cfg->index);

            rgnAddr.data.startAddr = cfg->address;
            Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_ADDR + (0x40u * (uint32_t)cfg->index), rgnAddr.reg);

            rgnSize.data.imgSize = cfg->size;
            Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_SIZE + (0x40u * (uint32_t)cfg->index), rgnSize.reg);

            rgnVld.data.valid = cfg->valid;
            Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_VLD + (0x40u * (uint32_t)cfg->index), rgnVld.reg);

            Mram_ApbWritel(host, MRAM_IMG_CRC_RGN_EXPVAL + (0x40u * (uint32_t)cfg->index), cfg->crcExpval);

            cfgRet = MRAM_RET_OK;
        }
    }

    return cfgRet;
}

/********************************************************************************************************
 * \brief This function triggered the crc checking for image region.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostImageCrcCheckStart(
 *                          Mram_HostType *host, uint8_t index, Mram_CrcCheckModeType mode)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      index - The index of image region
 *                      mode - Mode of triggering
 *                              MRAM_IMG_CRC_CHK_INVALID: Invalid the region
 *                              MRAM_IMG_CRC_CHK_STOP: Stop check
 *                              MRAM_IMG_CRC_CHK_ONESHOT: Check once and return results immediately
 *                              MRAM_IMG_CRC_CHK_LOOP: Background loop check, get results through interrupt or active interface
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Trigger success
 *                      Others - Trigger failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostImageCrcCheckStart(Mram_HostType *host, uint8_t index, Mram_CrcCheckModeType mode)
{
    int32_t ret = MRAM_RET_OK;

    if (index >= MRAM_IMG_CRC_REGION_NUM) {
        ret = MRAM_RET_BOUND_ERR;
    }
    else {
        switch (mode) {
        case MRAM_IMG_CRC_CHK_INVALID:
            /* If you need to reconfigure this area, set it to invalid */
            Mram_CrcCheckInvalid(host, index);
            break;
        case MRAM_IMG_CRC_CHK_STOP:
            /* For loop mode, stop the check */
            Mram_CrcCheckStopTrigger(host, index);
            break;
        case MRAM_IMG_CRC_CHK_ONESHOT:
            /* one shot mode */
            ret = Mram_CrcCheckOneshotTrigger(host, index);
            break;
        case MRAM_IMG_CRC_CHK_LOOP:
            /* loop mode */
            ret = Mram_CrcCheckLoopTrigger(host, index);
            break;
        default:
            /* The mode is not supported */
            ret = MRAM_RET_PARAMETER_ERR;
            break;
        }
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function returns the check result of the specified region.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostGetCrcCheckStatus(
 *                          Mram_HostType *host, uint8_t index)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      index - The index of region
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Check crc correctly
 *                      MRAM_RET_BOUND_ERR - Parameter error
 *                      MRAM_RET_CRC_ERR - crc check error
 *
 * Description        : It is usually used by loopmode to get the result, because onshot returns
 *                      the result on the spot after firing.
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostGetCrcCheckStatus(Mram_HostType *host, uint8_t index)
{
    int32_t ret = MRAM_RET_OK;
    Mram_ImgCrcRgnCfgType rgnCfg;

    if (index >= MRAM_IMG_CRC_REGION_NUM) {
        ret = MRAM_RET_BOUND_ERR;
    }
    else {
        rgnCfg.reg = Mram_ApbReadl(host, MRAM_IMG_CRC_RGN_CFG + (0x40u * (uint32_t)index));
        if (0u != rgnCfg.data.crcFail) {
            Mram_CrcCheckStopTrigger(host, index);
            ret = MRAM_RET_CRC_ERR;
        }
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function send tmc cmd as configured.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostSendTmc(
 *                          Mram_HostType *host, Mram_TmcCmdCfgType* tmcCfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : tmcCfg - Configure tmc cmd, The command to read the direction returns the
 *                               data to this structure
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Command sent successfully
 *                      Others - Command sending failure
 *
 * Description        : The fuse bit mfg_disable cannot be set when the mram macro is read or
 *                      written in this function
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostSendTmc(Mram_HostType *host, Mram_TmcCmdCfgType* tmcCfg)
{
    int32_t ret;

    switch (tmcCfg->type) {
    case MRAM_TMC_CMD_TYPE_READ:
        /* The fixed read length is 16B */
        ret = Mram_TmcReadWord(host, &(tmcCfg->param.rdWrWordCfg));
        break;
    case MRAM_TMC_CMD_TYPE_WRITE:
        /* The fixed write length is 16B */
        ret = Mram_TmcWriteWord(host, &(tmcCfg->param.rdWrWordCfg));
        break;
    case MRAM_TMC_CMD_TYPE_READ_REG32:
        /* Read a tmc register */
        ret = Mram_TmcReadRegByApb(host, &(tmcCfg->param.rdWrRegCfg));
        break;
    case MRAM_TMC_CMD_TYPE_WRITE_REG32:
        /* Write a tmc register */
        ret = Mram_TmcWriteRegByApb(host, &(tmcCfg->param.rdWrRegCfg));
        break;
#if MRAM_USE_APB2TMB
    case MRAM_TMC_CMD_TYPE_READ_BIST:
        ret = Mram_TmcReadByBist(host, &(tmcCfg->param.rdWrWordCfg));
        break;
    case MRAM_TMC_CMD_TYPE_WRITE_BIST:
        ret = Mram_TmcWriteByBist(host, &(tmcCfg->param.rdWrWordCfg));
        break;
    case MRAM_TMC_CMD_TYPE_READ_REG32_BIST:
        ret = Mram_TmcReadRegByBist(host, &(tmcCfg->param.rdWrRegCfg));
        break;
    case MRAM_TMC_CMD_TYPE_WRITE_REG32_BIST:
        ret = Mram_TmcWriteRegByBist(host, &(tmcCfg->param.rdWrRegCfg));
        break;
#endif /* #if MRAM_USE_APB2TMB */
    default:
        ret = MRAM_RET_UNSUPPORT;
        break;
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function refresh wrbuf in the configured mode.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostWrBufferFlush(
 *                          Mram_HostType *host, Mram_FlushModeType mode)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      mode - Refreshed mode.
 *                             MRAM_FLUSH_ONLY_CURRENT:Only the existing ones in the current buffer
 *                             are refreshed
 *                             MRAM_FLUSH_UNTIL_EMPTY:Refresh buffer until buffer is empty
 *                             MRAM_FLUSH_UNTIL_EMPTY_AND_SUSPEND:Refresh buffer until buffer is
 *                             empty, And block new writes to wrbuf
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Refresh successfully
 *                      Others - Refresh failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostWrBufferFlush(Mram_HostType *host, Mram_FlushModeType mode)
{
    int32_t ret = MRAM_RET_OK;
    Mram_Axi2tmcWrCfgType wrCfg;
    Mram_Axi2tmcWrbufCfgType wrbufCfg;

    wrCfg.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_WR_CFG);

    if (wrCfg.data.wrbufDis == 1u) {
        /* Do nothing */
    } else {
        wrbufCfg.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_WRBUF_CFG);
        wrbufCfg.data.flush = 1u;
        wrbufCfg.data.flushMode = (MRAM_FLUSH_ONLY_CURRENT != mode) ? 1u : 0u;
        wrbufCfg.data.flushDisAw = (MRAM_FLUSH_UNTIL_EMPTY_AND_SUSPEND == mode) ? 1u : 0u;

        Mram_ApbWritel(host, MRAM_AXI2TMC_WRBUF_CFG, wrbufCfg.reg);

        /* Wait for refresh to complete */
        ret = Mram_WaitForBitTimes(host->config.apbBase + MRAM_MRAMC_FUNC_INT0_STA,
            MRAM_MRAMC_FUNC_INT0_STA_WRBUF_FLUSH_DONE_MASK, false,
            MRAM_MAX_POLLING_FLUSH_DONE_TIMEOUT);
        wrbufCfg.data.flushMode = 0u;
        wrbufCfg.data.flushDisAw = 0u;
        wrbufCfg.data.flush = 0u;

        /* You need to turn off the refresh before you can clear the status */
        Mram_ApbWritel(host, MRAM_AXI2TMC_WRBUF_CFG, wrbufCfg.reg);
        Mram_ApbWritel(host, MRAM_MRAMC_FUNC_INT0_STA, MRAM_MRAMC_FUNC_INT0_STA_WRBUF_FLUSH_DONE_MASK);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function complete a refcell rewrite operation.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostRefCellRewriteEnable(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Successful
 *                      Others - Operation failure
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostRefCellRewriteEnable(Mram_HostType *host)
{
    int32_t ret = MRAM_RET_FAILED;
    uint8_t data[16];
    Mram_IntFlagType flag;
    Mram_TmcRdWrWordCfgType cfg;

    /* PRQA S 0315 3 */
    (void) MRAM_MEMCLR(data, 16u);
    (void) MRAM_MEMCLR(&flag, sizeof(Mram_IntFlagType));
    (void) MRAM_MEMCLR(&cfg, sizeof(Mram_TmcRdWrWordCfgType));

    cfg.type = MRAM_BLOCK_TYPE_INFO_MARCO;
    cfg.addr = 0x10UL << 4;
    cfg.buffer = data;
    cfg.info = 0u;
    data[0] = MRAM_REL_CELL_REWRITE_ENABLE_CODE & 0xFFu;
    data[1] = (MRAM_REL_CELL_REWRITE_ENABLE_CODE >> 8) & 0xFFu;

    /* There are expected interruptions to the process, which will be ignored here */
    Mram_DisableIrq(host, &flag);
    /* Confirm that the 55aa trigger mode is used */
    if (MRAM_RET_OK != Mram_RefCellRewriteSetup(host, MRAM_TMC_CMD_REWRITE_DISABLE_BY_55AA)) {
        MRAM_LOG_ERROR("Mram{0x%x} Mram_RefCellRewriteSetup failed\r\n", host->config.apbBase);
    }
    /* Write 55aa, the next time autowakup is triggered */
    else if (MRAM_RET_OK != Mram_TmcWriteWord(host, &cfg)) {
        MRAM_LOG_ERROR("Mram{0x%x} Mram_TmcWriteWord failed\r\n", host->config.apbBase);
    }
    /* The software triggers an autowakup */
    else if (MRAM_RET_OK != Mram_AutoWakeup(host)) {
        MRAM_LOG_ERROR("Mram{0x%x} Mram_AutoWakeup failed\r\n", host->config.apbBase);
    }
    /* Read back info to check whether the rewrite succeeds */
    else if (MRAM_RET_OK != Mram_TmcReadWord(host, &cfg)) {
        MRAM_LOG_ERROR("Mram{0x%x} Mram_TmcReadWord failed\r\n", host->config.apbBase);
    }
    else if ((data[0] != (MRAM_REL_CELL_REWRITE_DISABLE_CODE & 0xFFu)) ||
            (data[1] != ((MRAM_REL_CELL_REWRITE_DISABLE_CODE >> 8) & 0xFFu))) {
        MRAM_LOG_ERROR("Mram{0x%x} Flag is %x %x\r\n",
                host->config.apbBase, data[1], data[0]);
    }
    /* The autowakeup that triggers refcell rewrite has tmc error and cannot be cleared.
    Here we do autowakeup again to clear the tmc error */
    else if (MRAM_RET_OK != Mram_AutoWakeup(host)) {
        /* Do nothing */
    }
    else {
        ret = MRAM_RET_OK;
        /* Clear an expected error state */
        Mram_ApbWriteBits(host, MRAM_MRAMC_FUNC_INT0_STA,
            MRAM_MRAMC_FUNC_INT0_STA_TMC_RECALL_KEY_ERR_LSB, 1u, 1u);
    }

    /* Continue open interrupt */
    if ((flag.int0.reg != 0u) || (flag.int1.reg != 0u)) {
        Mram_EnableIrq(host, &flag);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function sets all data in the current readbuffer to invalid.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostRdBufferInvalidate(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Successful operation
 *                      Others - Operation failure
 *
 * Description        : The readbuffer of all open channels is cleared simultaneously.
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostRdBufferInvalidate(Mram_HostType *host)
{
    int32_t ret = MRAM_RET_OK;
    Mram_Axi2tmcRdCfgType rdCfg;

    rdCfg.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_RD_CFG);
    /* Set clear to 1 first */
    rdCfg.data.rdbufClr = 1u;
    Mram_ApbWritel(host, MRAM_AXI2TMC_RD_CFG, rdCfg.reg);
    /* Need to manually set the clear position to 0 */
    rdCfg.data.rdbufClr = 0u;
    Mram_ApbWritel(host, MRAM_AXI2TMC_RD_CFG, rdCfg.reg);
    return ret;
}

/********************************************************************************************************
 * \brief This function configure the area and switch for write protection.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostProtectSetup(
 *                          Mram_HostType *host, uint32_t addr, uint32_t size, bool enable)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      addr - Start address of write protection, 128K alignment required
 *                      size - Start address of write protection, 128K alignment required
 *                      enable - Write protect switch
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Configuration successful
 *                      Others - Configuration failure
 *
 * Description        : The address and length need to be aligned as required. If write protection
 *                      is triggered, an error is reported for the interrupt.
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostProtectSetup(Mram_HostType *host, uint32_t addr, uint32_t size, bool enable)
{
    int32_t ret = MRAM_RET_OK;
    uint32_t reg;
    uint32_t mask = 0;
    uint32_t i;

    /* Determine the length of the write protection address */
    if (!MRAM_IS_ALIGNED(addr, MRAM_PROTECT_ALIGN_SIZE) || !MRAM_IS_ALIGNED(size, MRAM_PROTECT_ALIGN_SIZE)) {
        ret = MRAM_RET_PARAMETER_ERR;
    }
    else {
        /* Calculates the protection flag bit to be configured */
        for (i = (addr / MRAM_PROTECT_ALIGN_SIZE); i < ((addr + size) / MRAM_PROTECT_ALIGN_SIZE); i++) {
            mask |= MRAM_BIT(i);
        }

        reg = Mram_ApbReadl(host, MRAM_MRAM_SWPGLK);

        /* Switch protection flag bit */
        if (true == enable) {
            reg |= mask;
        }
        else {
            reg &= (~mask);
        }

        Mram_PglkPasswordSetup(host, host->pglkPassword);
        Mram_ApbWritel(host, MRAM_MRAM_SWPGLK, reg);
        /* After the configuration is complete, set the key to 0 to disable the permission to modify the protection as */
        Mram_PglkPasswordSetup(host, 0ULL);
    }

    return ret;
}

/********************************************************************************************************
 * \brief This function convert the address from interleaved to the original address.
 *
 * \verbatim
 * Syntax             : uint32_t Mram_HostEccErrAddrConvert(
 *                          Mram_HostType *host, Mram_WordAddressType wordAddr)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      wordAddr - interleaved address
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : original address
 *
 * Description        : If fuse bit interleaveDis is off, the address will not be translated
 * \endverbatim
 *******************************************************************************************************/
uint32_t Mram_HostEccErrAddrConvert(Mram_HostType *host, Mram_WordAddressType wordAddr)
{
    Mram_WordAddressType convertAddr;
    convertAddr.data = 0u;
    if (host->config.interleaveDis == true) {
        /* There is no interleaved address. Assign the value directly */
        convertAddr.address.wordAddress = wordAddr.address.wordAddress;
    }
    else {
        if (MRAM_TYPE_32M == host->info.mramVersion) {
            /* Mram32mb macro Address translation */
            convertAddr.original32MAddress.yAddress = wordAddr.interleaved32MAddress.yAddress;
            convertAddr.original32MAddress.xAddress3To0 = wordAddr.interleaved32MAddress.xAddress3To0;
            convertAddr.original32MAddress.xAddress9To4 = wordAddr.interleaved32MAddress.xAddress9To4;
            convertAddr.original32MAddress.bAddress = wordAddr.interleaved32MAddress.bAddress;
        }
        else {
            /* Mram4mb macro Address translation */
            convertAddr.original4MAddress.yAddress = wordAddr.interleaved4MAddress.yAddress;
            convertAddr.original4MAddress.xAddressBit0 = wordAddr.interleaved4MAddress.xAddressBit0;
            convertAddr.original4MAddress.xAddress9To1 = wordAddr.interleaved4MAddress.xAddress9To1;
        }
    }
    MRAM_LOG_DEBUG("Address convert: 0x%x 0x%x interleaveDis(%d) Type(%d)\r\n",
        wordAddr.address.wordAddress, convertAddr.address.wordAddress,
        host->config.interleaveDis, host->info.mramVersion);
    /* The final address returned is word address, multiplied by 16 to get AXI address */
    return convertAddr.address.wordAddress;
}

/********************************************************************************************************
 * \brief This function be prepared to shut down.
 *
 * \verbatim
 * Syntax             : void Mram_HostDeinit(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Close the open crc insistence and refresh writebuffer and readbuffer
 * \endverbatim
 *******************************************************************************************************/
void Mram_HostDeinit(Mram_HostType *host)
{
    uint8_t i;
    Mram_RdBufferCfgType rdbufferTemp;
    /* PRQA S 0315 1 */
    (void) MRAM_MEMCLR(&rdbufferTemp, sizeof(Mram_RdBufferCfgType));
    rdbufferTemp.enable = false;
    /* All data from wrbuffer is flushed into the mram macro */
    if (MRAM_RET_OK != Mram_HostWrBufferFlush(host, MRAM_FLUSH_UNTIL_EMPTY_AND_SUSPEND)) {
        MRAM_LOG_ERROR("Mram[0x%x] wrbuf flush failed when deinit\r\n", host->config.apbBase);
    }

    for (i = 0; i < MRAM_IMG_CRC_REGION_NUM; i++) {
        /* Stop all crc checks and set them to invalid */
        Mram_CrcCheckInvalid(host, i);
    }

    for (i = 0; i < MRAM_RDBUFFER_NUM; i++) {
        rdbufferTemp.channelId = i;
        /* Close all readbuffers */
        (void) Mram_RdbufferChannelConfig(host, &rdbufferTemp);
    }
}

/********************************************************************************************************
 * \brief This function returns the number of remaining writebuffer entries.
 *
 * \verbatim
 * Syntax             : uint32_t Mram_HostGetWrbufferEntry(
 *                          Mram_HostType *host)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Return the remaining space, in bytes, of the current wriebuffer.
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
uint32_t Mram_HostGetWrbufferEntry(Mram_HostType *host)
{
    Mram_Axi2tmcWrbufCfgType wrbufCfg;
    wrbufCfg.reg = Mram_ApbReadl(host, MRAM_AXI2TMC_WRBUF_CFG);
#if defined(ARCH_armv8_r) || defined(CONFIG_ARMV8R)
    return ((MRAM_AXI2TMC_WRBUF_CFG_MAX_ENTRY - wrbufCfg.data.entry) << 4);
#else
    return ((MRAM_AXI2TMC_WRBUF_CFG_MAX_ENTRY - wrbufCfg.data.entry) << 3);
#endif
}

/********************************************************************************************************
 * \brief This function read data from the mram macro.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostRead(
 *                          Mram_HostType *host, uint32_t address,
 *                          uint8_t* buf,uint32_t size)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      address - Address of the mram
 *                      size - Data size
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : buf - The destination address of the read data
 *
 * Return value       : MRAM_RET_OK - Read successfully
 *                      MRAM_RET_BOUND_ERR - Parameter transgression
 *
 * Description        : The CPU cache needs to be maintained on the upper layer
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostRead(Mram_HostType *host, uint32_t address, uint8_t* buf,uint32_t size)
{
    int32_t ret = MRAM_RET_OK;
    if ((address + size) > host->info.totalSize) {
        /* Access out of bounds, directly report an error exit */
        ret = MRAM_RET_BOUND_ERR;
    }
    else {
        /* Direct access to bus address for data transfer */
        (void) Mram_HostMemcopy((void *)buf, (void *)(host->config.axiRdBase + address), size);
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function Write data to the mram macro.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostWrite(
 *                          Mram_HostType *host, uint32_t address,
 *                          const uint8_t* buf,uint32_t size)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      address - Address of the mram
 *                      buf - Source address of the write data
 *                      size - Data size
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Write successfully
 *                      MRAM_RET_BOUND_ERR - Parameter transgression
 *
 * Description        : The CPU cache needs to be maintained on the upper layer
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostWrite(Mram_HostType *host, uint32_t address, const uint8_t* buf,uint32_t size)
{
    int32_t ret = MRAM_RET_OK;
    if ((address + size) > host->info.totalSize) {
        /* Access out of bounds, directly report an error exit */
        ret = MRAM_RET_BOUND_ERR;
    }
    else {
        /* Direct access to bus address for data transfer */
        (void) Mram_HostMemcopy((void *)(host->config.axiWrBase + address), (const void *)buf, size);
    }
    return ret;
}

/********************************************************************************************************
 * \brief This function registers listening callbacks for interrupt events.
 *
 * \verbatim
 * Syntax             : int32_t Mram_HostEventSetup(
 *                              Mram_HostType *host, Mram_EventListenerType *eventListener, void *eventListenerArg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : host - An abstract handle to the hardware device
 *                      eventListener - Callback function
 *                      eventListenerArg - Parameters of the callback function
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : MRAM_RET_OK - Registered successfully
 *
 * Description        : None
 * \endverbatim
 *******************************************************************************************************/
int32_t Mram_HostEventSetup(Mram_HostType *host, Mram_EventListenerType *eventListener,
        void *eventListenerArg)
{
    host->eventListener = eventListener;
    host->eventListenerArg = (eventListenerArg != NULL) ? eventListenerArg : (void *)host;
    return MRAM_RET_OK;
}

#ifdef CFG_PLATFORM_MCAL
#define EEP_STOP_SEC_CODE
#include "Eep_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

/* End of file */
